merge test
authorDhananjay Sathe <dhananjaysathe@gmail.com>
Thu, 21 Jul 2011 19:27:16 +0000 (00:57 +0530)
committerDhananjay Sathe <dhananjaysathe@gmail.com>
Thu, 21 Jul 2011 19:27:16 +0000 (00:57 +0530)
21 files changed:
build/lib.linux-x86_64-2.7/sambagtk/__init__.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/atsvc.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/dialogs.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/main.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/objects.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/pygwcrontab.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/pygwregedit.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/pygwsam.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/pygwsvcctl.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/registry.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/sam.py [new file with mode: 0644]
build/lib.linux-x86_64-2.7/sambagtk/svcctl.py [new file with mode: 0644]
build/scripts-2.7/gepdump [new file with mode: 0644]
build/scripts-2.7/gregedit [new file with mode: 0644]
build/scripts-2.7/gtkldb [new file with mode: 0644]
merge_test [new file with mode: 0644]
sambagtk/dialogs.py
sambagtk/images/samba-logo-small.png [new file with mode: 0644]
sambagtk/main.glade [new file with mode: 0644]
sambagtk/pygwsam.py
sambagtk/sam.py

diff --git a/build/lib.linux-x86_64-2.7/sambagtk/__init__.py b/build/lib.linux-x86_64-2.7/sambagtk/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/atsvc.py b/build/lib.linux-x86_64-2.7/sambagtk/atsvc.py
new file mode 100644 (file)
index 0000000..33963bf
--- /dev/null
@@ -0,0 +1,649 @@
+import datetime
+import gtk
+import os
+import sys
+
+class Task(object):
+
+    def __init__(self, command, id):
+        self.command = command
+        self.id = id
+        self.job_time = 0
+        self.days_of_month = 0
+        self.days_of_week = 0
+        self.run_periodically = False
+        self.non_interactive = False
+
+    def get_scheduled_index(self):
+        if (self.days_of_month == 0x7FFFFFFF): # daily schedule
+            return 0
+        elif (self.days_of_week > 0): # weekly schedule
+            return 1
+        else: # monthly schedule
+            return 2
+
+    def get_time(self):
+        time = self.job_time / 1000 # get rid of milliseconds
+        seconds = int(time % 60)
+
+        time /= 60 # get rid of seconds
+        minutes = int(time % 60)
+
+        time /= 60 # get rid of minutes
+        hour = int(time % 24)
+
+        return (hour, minutes, seconds)
+
+    def set_time(self, hour, minutes, seconds):
+        h_ms = int(hour * 60 * 60 * 1000)
+        m_ms = int(minutes * 60 * 1000)
+        s_ms = int(seconds * 1000)
+
+        self.job_time = h_ms + m_ms + s_ms
+
+    def get_scheduled_days_of_week(self):
+        dow_list = []
+
+        for day_no in xrange(0, 7):
+            if (self.days_of_week & (2 ** day_no)):
+                dow_list.append(day_no)
+
+        return dow_list
+
+    def get_scheduled_days_of_month(self):
+        dom_list = []
+
+        for day_no in xrange(0, 31):
+            if (self.days_of_month & (2 ** day_no)):
+                dom_list.append(day_no)
+
+        return dom_list
+
+    def set_scheduled_days_of_week(self, dow_list):
+        self.days_of_week = 0x00
+
+        for day_no in dow_list:
+            self.days_of_week |= (2 ** day_no)
+
+    def set_scheduled_days_of_month(self, dom_list):
+        self.days_of_month = 0x00000000
+
+        for day_no in dom_list:
+            self.days_of_month |= (2 ** day_no)
+
+    def get_scheduled_description(self):
+        if (self.days_of_week == 0x00 and self.days_of_month == 0x00000000):
+            return "Not scheduled."
+
+        (hour, minutes, seconds) = self.get_time()
+        index = self.get_scheduled_index()
+
+        at_str = "%02d:%02d" % (hour, minutes)
+
+        if (self.run_periodically):
+            if (index == 0): # daily schedule
+                every_str = "every day"
+            elif (index == 1): # weekly schedule
+                dow_str = ""
+                for day_no in self.get_scheduled_days_of_week():
+                    dow_str += Task.get_day_of_week_name(day_no) + ", "
+
+                # eliminate the last comma
+                dow_str = dow_str.rstrip(", ")
+
+                every_str = "every " + dow_str + " of every week"
+            else: # monthly schedule
+                dom_str = ""
+                for day_no in self.get_scheduled_days_of_month():
+                    dom_str += Task.get_day_of_month_name(day_no) + ", "
+
+                # eliminate the last comma
+                dom_str = dom_str.rstrip(", ")
+
+                every_str = "every " + dom_str + " of every month"
+        else:
+            if (index == 0): # daily schedule
+                next_str = "once"
+            elif (index == 1): # weekly schedule
+                next_str = "next " + self.get_day_of_week_name(self.get_scheduled_days_of_week()[0])
+            else:
+                next_str = "next " + self.get_day_of_month_name(self.get_scheduled_days_of_month()[0]) + " of the month"
+
+        sw_str = "starting with " + str(datetime.date.today())
+
+        if (self.run_periodically):
+            return "At " + at_str + ", " + every_str + ", " + sw_str + "."
+        else:
+            return "At " + at_str + ", " + next_str + ", " + sw_str + "."
+
+    @staticmethod
+    def get_day_of_week_name(day_no):
+        DAYS_OF_WEEK = ["Monday", "Tuesday", "Wednesday", "Thursday",
+                "Friday", "Saturday", "Sunday"]
+
+        return DAYS_OF_WEEK[day_no]
+
+    @staticmethod
+    def get_day_of_month_name(day_no):
+        if day_no == 0:
+            return "1st"
+        elif day_no == 1:
+            return "2nd"
+        elif day_no == 2:
+            return "3rd"
+        else:
+            return str(day_no + 1) + "th"
+
+    def list_view_representation(self):
+        return [str(self.id), self.command, self.get_scheduled_description()]
+
+class TaskEditDialog(gtk.Dialog):
+
+    def __init__(self, task = None):
+        super(TaskEditDialog, self).__init__()
+
+        if (task is None):
+            self.brand_new = True
+            self.task = Task("", -1)
+        else:
+            self.brand_new = False
+            self.task = task
+
+        self.disable_signals = True
+
+        self.create()
+
+        if (not self.brand_new):
+            self.task_to_values()
+        self.update_sensitivity()
+        self.update_captions()
+
+        self.disable_signals = False
+
+    def create(self):
+        self.set_title(["Edit task", "New task"][self.brand_new])
+        self.set_border_width(5)
+        self.set_icon_from_file(os.path.join(sys.path[0], "images", "crontab.png"))
+        self.set_resizable(False)
+        self.set_size_request(500, -1)
+
+
+        # scheduled description label
+
+        self.scheduled_label = gtk.Label()
+        self.scheduled_label.set_line_wrap(True)
+        self.scheduled_label.set_padding(10, 10)
+        self.vbox.pack_start(self.scheduled_label, True, True, 0)
+
+        separator = gtk.HSeparator()
+        self.vbox.pack_start(separator, False, True, 10)
+
+
+        # command
+        hbox = gtk.HBox()
+        self.vbox.pack_start(hbox, False, False, 10)
+
+        label = gtk.Label("Command:")
+        hbox.pack_start(label, False, True, 5)
+
+        self.command_entry = gtk.Entry()
+        self.command_entry.set_activates_default(True)
+        hbox.pack_start(self.command_entry, True, True, 5)
+
+        separator = gtk.HSeparator()
+        self.vbox.pack_start(separator, False, True, 10)
+
+        table = gtk.Table(2, 3)
+        table.set_border_width(5)
+        table.set_row_spacings(5)
+        table.set_col_spacings(5)
+        self.vbox.pack_start(table, True, True, 0)
+
+        label = gtk.Label("Schedule Task:")
+        table.attach(label, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        label = gtk.Label("Start Time:")
+        table.attach(label, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
+
+        table.attach(gtk.Label(), 2, 3, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        self.scheduled_combo = gtk.combo_box_new_text()
+        self.scheduled_combo.append_text("Daily")
+        self.scheduled_combo.append_text("Weekly")
+        self.scheduled_combo.append_text("Monthly")
+        self.scheduled_combo.set_active(0)
+        table.attach(self.scheduled_combo, 0, 1, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        hbox = gtk.HBox()
+        table.attach(hbox, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 10, 0)
+
+        self.hour_spin_button = gtk.SpinButton()
+        self.hour_spin_button.set_range(0, 23)
+        self.hour_spin_button.set_numeric(True)
+        self.hour_spin_button.set_increments(1, 1)
+        self.hour_spin_button.set_width_chars(2)
+        hbox.pack_start(self.hour_spin_button, True, True, 0)
+
+        label = gtk.Label(":")
+        hbox.pack_start(label, False, False, 0)
+
+        self.minute_spin_button = gtk.SpinButton()
+        self.minute_spin_button.set_range(0, 59)
+        self.minute_spin_button.set_numeric(True)
+        self.minute_spin_button.set_increments(1, 1)
+        self.minute_spin_button.set_width_chars(2)
+        hbox.pack_start(self.minute_spin_button, True, True, 0)
+
+        table.attach(gtk.Label(), 2, 3, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        table = gtk.Table(2, 2)
+        self.vbox.pack_start(table, True, True, 5)
+
+
+        # weekly stuff
+
+        self.weekly_label = gtk.Label(" Run weekly on: ")
+        table.attach(self.weekly_label, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_border_width(5)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        table.attach(scrolledwindow, 0, 1, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 5, 5)
+
+        self.weekly_tree_view = gtk.TreeView()
+        self.weekly_tree_view.set_size_request(0, 150)
+        self.weekly_tree_view.set_headers_visible(False)
+        scrolledwindow.add(self.weekly_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Checked")
+        self.weekly_toggle_renderer = gtk.CellRendererToggle()
+        column.pack_start(self.weekly_toggle_renderer, True)
+        self.weekly_tree_view.append_column(column)
+        column.add_attribute(self.weekly_toggle_renderer, "active", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Day")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.weekly_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        self.weekly_store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING)
+        self.weekly_tree_view.set_model(self.weekly_store)
+
+        self.weekly_store.append([False, "Monday"])
+        self.weekly_store.append([False, "Tuesday"])
+        self.weekly_store.append([False, "Wednesday"])
+        self.weekly_store.append([False, "Thursday"])
+        self.weekly_store.append([False, "Friday"])
+        self.weekly_store.append([False, "Saturday"])
+        self.weekly_store.append([False, "Sunday"])
+
+
+        # monthly stuff
+
+        self.monthly_label = gtk.Label(" Run monthly on the: ")
+        table.attach(self.monthly_label, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_border_width(5)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        table.attach(scrolledwindow, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 5, 5)
+
+        self.monthly_tree_view = gtk.TreeView()
+        self.monthly_tree_view.set_headers_visible(False)
+        scrolledwindow.add(self.monthly_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Checked")
+        self.monthly_toggle_renderer = gtk.CellRendererToggle()
+        column.pack_start(self.monthly_toggle_renderer, True)
+        self.monthly_tree_view.append_column(column)
+        column.add_attribute(self.monthly_toggle_renderer, "active", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Day")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.monthly_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        self.monthly_store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING)
+        self.monthly_tree_view.set_model(self.monthly_store)
+
+        for day_no in xrange(0, 31):
+            self.monthly_store.append([False, Task.get_day_of_month_name(day_no)])
+
+        self.run_periodically_check = gtk.CheckButton("Repeating schedule (run periodically)")
+        self.run_periodically_check.connect("toggled", self.on_update_captions)
+        self.vbox.pack_start(self.run_periodically_check, False, True, 5)
+
+        self.non_interactive_check = gtk.CheckButton("Don't interact with the logged-on user")
+        self.vbox.pack_start(self.non_interactive_check, False, True, 5)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new task
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.scheduled_combo.connect("changed", self.on_update_sensitivity)
+        self.scheduled_combo.connect("changed", self.on_update_captions)
+        self.weekly_toggle_renderer.connect("toggled", self.on_renderer_toggled, self.weekly_store)
+        self.monthly_toggle_renderer.connect("toggled", self.on_renderer_toggled, self.monthly_store)
+        self.hour_spin_button.connect("value-changed", self.on_update_captions)
+        self.minute_spin_button.connect("value-changed", self.on_update_captions)
+
+    def check_for_problems(self):
+        if (len(self.command_entry.get_text().strip()) == 0):
+            return "Please specify a command."
+
+        index = self.scheduled_combo.get_active()
+        last_active_row = None
+
+        if (index == 1): # weekly schedule
+            for row in self.weekly_store:
+                if (row[0]):
+                    last_active_row = row
+                    break
+            if (last_active_row is None):
+                return "You need to select at least one day of the week, for a weekly schedule."
+        elif (index == 2): # monthly schedule
+            for row in self.monthly_store:
+                if (row[0]):
+                    last_active_row = row
+                    break
+            if (last_active_row is None):
+                return "You need to select at least one day of the month, for a monthly schedule."
+
+        return None
+
+    def update_sensitivity(self):
+        index = self.scheduled_combo.get_active()
+
+        self.weekly_label.set_sensitive(index == 1) # weekly
+        self.monthly_label.set_sensitive(index == 2) # monthly
+
+        self.weekly_tree_view.set_sensitive(index == 1) # weekly
+        self.monthly_tree_view.set_sensitive(index == 2) # monthly
+
+    def update_captions(self):
+        self.values_to_task()
+        self.scheduled_label.set_text(self.task.get_scheduled_description())
+
+        if (self.run_periodically_check.get_active()):
+            self.weekly_label.set_label(" Run weekly on: ")
+            self.monthly_label.set_label(" Run monthly on the: ")
+
+            self.weekly_toggle_renderer.set_property("radio", False)
+            self.monthly_toggle_renderer.set_property("radio", False)
+        else:
+            self.weekly_label.set_label(" Run on next: ")
+            self.monthly_label.set_label(" Run on the next: ")
+
+            self.weekly_toggle_renderer.set_property("radio", True)
+            self.monthly_toggle_renderer.set_property("radio", True)
+
+            # make sure there's exactly one checked item when working with radios
+            first_active_row = None
+            for row in self.weekly_store:
+                if (row[0]):
+                    if (first_active_row is not None):
+                        row[0] = False
+                    else:
+                        first_active_row = row
+            if (first_active_row is None):
+                self.weekly_store[0][0] = True
+
+            # make sure there's exactly one checked item when working with radios
+            first_active_row = None
+            for row in self.monthly_store:
+                if (row[0]):
+                    if (first_active_row is not None):
+                        row[0] = False
+                    else:
+                        first_active_row = row
+            if (first_active_row is None):
+                self.monthly_store[0][0] = True
+
+        # needed for an immediate visual update
+        self.weekly_tree_view.queue_draw()
+        self.monthly_tree_view.queue_draw()
+
+    def task_to_values(self):
+        if (self.task is None):
+            raise Exception("task not set")
+
+        self.scheduled_label.set_text(self.task.get_scheduled_description())
+        self.command_entry.set_text(self.task.command)
+
+        (hour, minutes, seconds) = self.task.get_time()
+
+        self.hour_spin_button.set_value(hour)
+        self.minute_spin_button.set_value(minutes)
+
+        index = self.task.get_scheduled_index()
+        self.scheduled_combo.set_active(index)
+        if (index == 1): # weekly schedule
+            for day_no in self.task.get_scheduled_days_of_week():
+                self.weekly_store[day_no][0] = True
+        elif (index == 2): # monthly schedule
+            for day_no in self.task.get_scheduled_days_of_month():
+                self.monthly_store[day_no][0] = True
+
+        self.run_periodically_check.set_active(self.task.run_periodically)
+        self.non_interactive_check.set_active(self.task.non_interactive)
+
+    def values_to_task(self):
+        if (self.task is None):
+            raise Exception("task not set")
+
+        self.task.command = self.command_entry.get_text()
+
+        self.task.set_time(self.hour_spin_button.get_value(), self.minute_spin_button.get_value(), 0)
+
+        index = self.scheduled_combo.get_active()
+
+        self.task.set_scheduled_days_of_week([])
+        self.task.set_scheduled_days_of_month([])
+
+        if (index == 0): # daily schedule
+            dom_list = []
+            for day_no in xrange(0, 31):
+                dom_list.append(day_no)
+            self.task.set_scheduled_days_of_month(dom_list)
+        elif (index == 1): # weekly schedule
+            dow_list = []
+            for day_no in xrange(0, 7):
+                if (self.weekly_store[day_no][0]):
+                    dow_list.append(day_no)
+            self.task.set_scheduled_days_of_week(dow_list)
+        elif (index == 2): # monthly schedule
+            dom_list = []
+            for day_no in xrange(0, 31):
+                if (self.monthly_store[day_no][0]):
+                    dom_list.append(day_no)
+            self.task.set_scheduled_days_of_month(dom_list)
+
+        self.task.run_periodically = self.run_periodically_check.get_active()
+        self.task.non_interactive = self.non_interactive_check.get_active()
+
+    def on_renderer_toggled(self, widget, path, store):
+        if (self.disable_signals):
+            return
+
+        if (widget.get_radio()):
+            for row in store:
+                row[0] = False
+            store[path][0] = True
+        else:
+            store[path][0] = not store[path][0]
+
+        self.update_captions()
+
+    def on_update_sensitivity(self, widget):
+        if (self.disable_signals):
+            return
+
+        self.update_sensitivity()
+
+    def on_update_captions(self, widget):
+        if (self.disable_signals):
+            return
+
+        self.update_captions()
+
+
+class ATSvcConnectDialog(gtk.Dialog):
+
+    def __init__(self, server, transport_type, username, password):
+        super(ATSvcConnectDialog, self).__init__()
+
+        self.server_address = server
+        self.transport_type = transport_type
+        self.username = username
+        self.password = password
+
+        self.create()
+
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title("Connect to a server")
+        self.set_border_width(5)
+        self.set_icon_name(gtk.STOCK_CONNECT)
+        self.set_resizable(False)
+
+        # server frame
+
+        self.vbox.set_spacing(5)
+
+        self.server_frame = gtk.Frame("Server")
+        self.vbox.pack_start(self.server_frame, False, True, 0)
+
+        table = gtk.Table(3, 2)
+        table.set_border_width(5)
+        self.server_frame.add(table)
+
+        label = gtk.Label(" Server address: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.server_address_entry = gtk.Entry()
+        self.server_address_entry.set_text(self.server_address)
+        self.server_address_entry.set_activates_default(True)
+        table.attach(self.server_address_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Username: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.username_entry = gtk.Entry()
+        self.username_entry.set_text(self.username)
+        self.username_entry.set_activates_default(True)
+        table.attach(self.username_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Password: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_text(self.password)
+        self.password_entry.set_visibility(False)
+        self.password_entry.set_activates_default(True)
+        table.attach(self.password_entry, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+
+        # transport frame
+
+        self.transport_frame = gtk.Frame(" Transport type ")
+        self.vbox.pack_start(self.transport_frame, False, True, 0)
+
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.transport_frame.add(vbox)
+
+        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP")
+        self.rpc_smb_tcpip_radio_button.set_active(self.transport_type == 0)
+        vbox.pack_start(self.rpc_smb_tcpip_radio_button)
+
+        self.rpc_tcpip_radio_button = gtk.RadioButton(self.rpc_smb_tcpip_radio_button, "RPC over TCP/IP")
+        self.rpc_tcpip_radio_button.set_sensitive(False)
+        self.rpc_tcpip_radio_button.set_active(self.transport_type == 1)
+        vbox.pack_start(self.rpc_tcpip_radio_button)
+
+        self.localhost_radio_button = gtk.RadioButton(self.rpc_tcpip_radio_button, "Localhost")
+        self.localhost_radio_button.set_sensitive(False)
+        self.localhost_radio_button.set_active(self.transport_type == 2)
+        vbox.pack_start(self.localhost_radio_button)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.connect_button = gtk.Button("Connect", gtk.STOCK_CONNECT)
+        self.connect_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.connect_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.rpc_smb_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.rpc_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.localhost_radio_button.connect("toggled", self.on_radio_button_toggled)
+
+    def update_sensitivity(self):
+        server_required = not self.localhost_radio_button.get_active()
+
+        self.server_address_entry.set_sensitive(server_required)
+
+    def get_server_address(self):
+        return self.server_address_entry.get_text().strip()
+
+    def get_transport_type(self):
+        if self.rpc_smb_tcpip_radio_button.get_active():
+            return 0
+        elif self.rpc_tcpip_radio_button.get_active():
+            return 1
+        elif self.localhost_radio_button.get_active():
+            return 2
+        else:
+            return -1
+
+    def get_username(self):
+        return self.username_entry.get_text().strip()
+
+    def get_password(self):
+        return self.password_entry.get_text()
+
+    def on_radio_button_toggled(self, widget):
+        self.update_sensitivity()
+
+
+
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/dialogs.py b/build/lib.linux-x86_64-2.7/sambagtk/dialogs.py
new file mode 100644 (file)
index 0000000..417ebf4
--- /dev/null
@@ -0,0 +1,51 @@
+# Samba GTK+ frontends
+# 
+# Copyright (C) 2010 Sergio Martins <sergio97@gmail.com>
+# Copyright (C) 2011 Jelmer Vernooij <jelmer@samba.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+import sys
+import os.path
+
+import gobject
+import gtk
+
+import samba
+
+class AboutDialog(gtk.AboutDialog):
+
+    def __init__(self, name, description, icon):
+        super(AboutDialog, self).__init__()
+
+        self.set_name(name)
+        self.set_version(samba.version)
+        self.set_logo(icon)
+        self.set_copyright("Copyright \xc2\xa9 2010 Sergio Martins <Sergio97@gmail.com>")
+        self.set_authors(["Sergio Martins <Sergio97@gmail.com>", "Calin Crisan <ccrisan@gmail.com>", "Jelmer Vernooij <jelmer@samba.org>"])
+        self.set_comments(description)
+        self.set_wrap_license(True)
+        self.set_license("""
+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 3 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, see <http://www.gnu.org/licenses/>.""")
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/main.py b/build/lib.linux-x86_64-2.7/sambagtk/main.py
new file mode 100644 (file)
index 0000000..83cb341
--- /dev/null
@@ -0,0 +1,497 @@
+'''
+Created on May 17, 2010
+
+@author: Sergio Martins
+'''
+import sys
+import pygtk
+pygtk.require20() #require pygtk version 2.0
+import gtk
+import gtk.glade
+import os.path
+import getopt
+
+import pygwsam
+import pygwregedit
+import pygwcrontab
+import pygwsvcctl
+
+from sambagtk.dialogs import AboutDialog
+from sambagtk.sam import SAMConnectDialog
+
+
+class SambaUtilities(object):
+
+    def __init__(self, connection_args={}, additional_connection_arguments={}):
+
+        self.create()
+
+        # these are the old windows of the utilities. We reparent the main
+        # widget so these arn't displayed but we need the handle so we can
+        # call functions and grab objects
+        self.sam_window = None
+        self.regedit_window = None
+        self.svcctl_window = None
+        self.crontab_window = None
+
+        self.connection_args = connection_args
+        self.additional_connection_args = {} #arguments not supported by all utilities, such as domain_index
+        self.additional_connection_args.update({"info_callback":self.server_info_callback}) #to save info or get updated info
+        self.print_redirect_sring = ""
+
+        self.update_sensitivity()
+        self.window.show()
+        self.push_status_message("Utility started successfully.")
+        self.utilites_notebook.grab_focus() #So switching to the regedit tab doesn't automatically focus the keys tree view
+
+        if (connection_args.has_key("connect_now") and connection_args["connect_now"]):
+            self.on_connect_all_button_clicked(None)
+
+
+    def create(self):
+        # get a builder and put it to work
+        builder = gtk.Builder()
+        builder.add_from_file("main.glade")
+
+        # dictionary for connections
+        connections = {"on_main_window_destroy": gtk.main_quit,
+                       "on_main_window_key_press_event": self.on_main_window_key_press_event,
+
+                       "on_connect_all_item_activate": self.on_connect_all_button_clicked,
+                       "on_disconnect_all_item_activate": self.on_disconnect_all_button_clicked,
+                       "on_quit_item_activate": self.on_quit_item_activate,
+                       "on_clear_log_activate": self.on_clear_log_activate,
+                       "on_connection_info_item_activate": self.on_connection_info_item_activate,
+                       "on_about_item_activate": self.on_about_item_activate,
+
+                       "on_connect_all_button_clicked": self.on_connect_all_button_clicked,
+                       "on_disconnect_all_button_clicked": self.on_disconnect_all_button_clicked,
+                       "on_clear_log_button_clicked": self.on_clear_log_activate,
+
+                       "on_utility_notebook_switch_page": self.on_utility_notebook_switch_page,
+
+                       }
+        #Make the connections
+        builder.connect_signals(connections)
+
+        #Handles
+        self.window = builder.get_object("main_window")
+        self.menubar_viewport = builder.get_object("menubar_viewport")
+        self.menubar = builder.get_object("menubar")
+        self.connect_all_item = builder.get_object("connect_all_item")
+        self.disconnect_all_item = builder.get_object("disconnect_all_item")
+
+        self.toolbar_viewport = builder.get_object("toolbar_viewport")
+        self.toolbar = builder.get_object("toolbar")
+        self.connect_all_button = builder.get_object("connect_all_button")
+        self.disconnect_all_button = builder.get_object("disconnect_all_button")
+
+        self.utilites_notebook = builder.get_object("utility_notebook")
+
+        self.server_label = builder.get_object("server_label")
+        self.username_label = builder.get_object("username_label")
+        self.status_label = builder.get_object("status_label")
+        self.messages_textview = builder.get_object("messages_textview")
+
+        self.sam_viewport = builder.get_object("sam_viewport")
+        self.svcctl_viewport = builder.get_object("svcctl_viewport")
+        self.crontab_viewport = builder.get_object("crontab_viewport")
+        self.regedit_viewport = builder.get_object("regedit_viewport")
+
+        self.progressbar = builder.get_object("progressbar")
+        self.statusbar = builder.get_object("statusbar")
+
+
+    def init_sam_page(self):
+
+        args = self.connection_args.copy()
+        if self.additional_connection_args.has_key("domain_index"):
+            args.update({"domain_index":self.additional_connection_args["domain_index"]})
+        if self.additional_connection_args.has_key("info_callback"):
+            args.update({"info_callback":self.additional_connection_args["info_callback"]})
+
+        self.sam_window = pygwsam.SAMWindow(**args) #start up the utility
+        self.sam_window.users_groups_notebook.reparent(self.sam_viewport) #reparent the main widget into a notebook tab
+        self.sam_viewport.show_all() #unhide all widgets
+
+        #We'll be displaying this later. We need to unparent it before attaching it to another container
+        self.sam_window.menubar.unparent()
+        self.sam_window.toolbar.unparent()
+        self.sam_window.statusbar = self.statusbar #we simply tell the utility to use our status bar instead
+
+        self.set_status("User tab initialized.")
+        self.update_sensitivity()
+
+    def init_regedit_page(self):
+        args = self.connection_args.copy()
+        if self.additional_connection_args.has_key("info_callback"):
+            args.update({"info_callback":self.additional_connection_args["info_callback"]})
+        self.regedit_window = pygwregedit.RegEditWindow(**args) #start up the utility
+        self.regedit_window.hpaned.reparent(self.regedit_viewport) #reparent the main widget into a notebook tab
+        self.regedit_viewport.show_all() #unhide all widgets
+
+        self.regedit_window.menubar.unparent()
+        self.regedit_window.toolbar.unparent()
+        self.regedit_window.progressbar = self.progressbar
+        self.regedit_window.statusbar = self.statusbar
+
+        self.set_status("Regedit tab initialized.")
+        self.update_sensitivity()
+
+    def init_svcctl_page(self):
+        args = self.connection_args.copy()
+        if self.additional_connection_args.has_key("info_callback"):
+            args.update({"info_callback":self.additional_connection_args["info_callback"]})
+        self.svcctl_window = pygwsvcctl.SvcCtlWindow(**args) #start up the utility
+        self.svcctl_window.scrolledwindow.reparent(self.svcctl_viewport) #reparent the main widget into a notebook tab
+        self.svcctl_viewport.show_all() #unhide all widgets
+
+        self.svcctl_window.menubar.unparent()
+        self.svcctl_window.toolbar.unparent()
+        self.svcctl_window.progressbar = self.progressbar
+        self.svcctl_window.statusbar = self.statusbar
+
+        self.set_status("Services tab initialized.")
+        self.update_sensitivity()
+
+    def init_crontab_page(self):
+        args = self.connection_args.copy()
+        if self.additional_connection_args.has_key("info_callback"):
+            args.update({"info_callback":self.additional_connection_args["info_callback"]})
+        self.crontab_window = pygwcrontab.CronTabWindow(**args) #start up the utility
+        self.crontab_window.scrolledwindow.reparent(self.crontab_viewport) #reparent the main widget into a notebook tab
+        self.crontab_viewport.show_all() #unhide all widgets
+
+        self.crontab_window.menubar.unparent()
+        self.crontab_window.toolbar.unparent()
+        self.crontab_window.statusbar = self.statusbar
+
+        self.set_status("Scheduled tasks tab initialized.")
+        self.update_sensitivity()
+
+    def sam_initialized(self):
+        return self.sam_window is not None
+
+    def regedit_initialized(self):
+        return self.regedit_window is not None
+
+    def svcctl_initialized(self):
+        return self.svcctl_window is not None
+
+    def crontab_initialized(self):
+        return self.crontab_window is not None
+
+    def update_sensitivity(self):
+        sam_connected = self.sam_initialized() and self.sam_window.connected()
+        regedit_connected = self.regedit_initialized() and self.regedit_window.connected()
+        svcctl_connected = self.svcctl_initialized() and self.svcctl_window.connected()
+        crontab_connected = self.crontab_initialized() and self.crontab_window.connected()
+        all_connected = sam_connected and regedit_connected and svcctl_connected and crontab_connected
+        all_disconnected = (not sam_connected) and (not regedit_connected) and (not svcctl_connected) and (not crontab_connected)
+
+        self.connect_all_button.set_sensitive(not all_connected)
+        self.disconnect_all_button.set_sensitive(not all_disconnected)
+        self.connect_all_item.set_sensitive(not all_connected)
+        self.disconnect_all_item.set_sensitive(not all_disconnected)
+
+        self.server_label.set_text(self.connection_args.has_key("server") and self.connection_args["server"] or "Unknown")
+        self.username_label.set_text(self.connection_args.has_key("username") and self.connection_args["username"] or "Unknwon")
+        if (all_connected):
+            self.status_label.set_text("All connected")
+        elif (all_disconnected):
+            self.status_label.set_text("All disconnected")
+        else:
+            connected_utilities = []
+            if sam_connected:
+                connected_utilities.append("User Manager")
+            if regedit_connected:
+                connected_utilities.append("Registry Editor")
+            if svcctl_connected:
+                connected_utilities.append("Services Manager")
+            if crontab_connected:
+                connected_utilities.append("Task Scheduler")
+            if len(connected_utilities) > 1:
+                connected_utilities[-1] = "and %s" % connected_utilities[-1]
+            self.status_label.set_text("%s %s" % (", ".join(connected_utilities), "connected."))
+
+    def server_info_callback(self, server = "", username = "", transport_type = None):
+        if server:
+            self.connection_args.update({"server":server})
+        if username:
+            self.connection_args.update({"username":username})
+        if transport_type:
+            self.connection_args.update({"transport_type":transport_type})
+
+    def run_message_dialog(self, type, buttons, message, parent = None):
+        if (parent == None):
+            parent = self.window
+
+        message_box = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, type, buttons, message)
+        response = message_box.run()
+        message_box.hide()
+
+        return response
+
+    def run_connect_all_dialog(self):
+        """Runs the connection dialog and saves connection arguments to self.connection_args
+
+        returns True if arguments were uptained successfully"""
+        #TODO in this function: handle domain selection
+        args = {}
+        #args and their default values
+        important_args = {"server":"", "username":"", "transport_type":0, }
+        for item in important_args.keys():
+                args.update(self.connection_args.has_key(item) and {item:self.connection_args[item]} or {item:important_args[item]})
+
+        dialog = SAMConnectDialog(**args)
+        dialog.show_all()
+
+        # loop to handle the failures
+        while True:
+            response_id = dialog.run()
+
+            if (response_id != gtk.RESPONSE_OK):
+                dialog.hide()
+                return False
+            else:
+                server = dialog.get_server_address()
+                username = dialog.get_username()
+                if server != "" and username != "":
+                    self.connection_args.update({"server":server})
+                    self.connection_args.update({"username":username})
+                    self.connection_args.update({"transport_type":dialog.get_transport_type()})
+                    self.connection_args.update({"password":dialog.get_password()})
+                    self.connection_args.update({"connect_now":True})
+                    self.additional_connection_args.update({"domain_index":0}) #TODO: get domain index
+                    break
+                else:
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "You must enter a server address and username.")
+
+
+        dialog.hide()
+        return True
+
+    def write(self, string): #Make this class a writeable object. Used so we can redirect print statements
+        if string == '\n':
+            self.push_status_message(self.print_redirect_sring)
+            print >>sys.__stdout__, self.print_redirect_sring #also print the string normally
+            self.print_redirect_sring = ""
+        else:
+            self.print_redirect_sring += string
+
+    def push_status_message(self, message):
+        """Pushes a message to the status textview in the main tab. This function inserts a \"\\n\" for you."""
+        buffer = self.messages_textview.get_buffer()
+        text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+        text += message + "\n"
+        buffer.set_text(text)
+
+        #scroll to the bottom
+        self.messages_textview.scroll_to_iter(buffer.get_end_iter(), 0.0)
+
+    def set_status(self, message):
+        self.statusbar.pop(0)
+        self.statusbar.push(0, message)
+        self.push_status_message(message)
+
+    def on_main_window_key_press_event(self, widget, event):
+        current_page = self.utilites_notebook.get_current_page()
+
+        if current_page == 1:
+            self.sam_window.on_key_press(widget, event)
+        elif current_page == 2:
+            self.regedit_window.on_key_press(widget, event)
+        elif current_page == 3:
+            self.svcctl_window.on_key_press(widget, event)
+        elif current_page == 4:
+            self.crontab_window.on_key_press(widget, event)
+
+    def on_utility_notebook_switch_page(self, widget, page, page_num):
+        if page_num == 0: #main page
+
+            #Menubar
+            children = self.menubar_viewport.get_children()
+            self.menubar_viewport.remove(children[0])
+            self.menubar_viewport.add(self.menubar)
+            self.menubar_viewport.show_all()
+            #Toolbar
+            children = self.toolbar_viewport.get_children()
+            self.toolbar_viewport.remove(children[0])
+            self.toolbar_viewport.add(self.toolbar)
+            self.toolbar_viewport.show_all()
+
+            self.update_sensitivity()
+
+        elif page_num == 1: #Sam page
+            if self.sam_viewport.child == None:
+                self.init_sam_page()
+
+            #Menubar
+            children = self.menubar_viewport.get_children()
+            self.menubar_viewport.remove(children[0])
+            self.menubar_viewport.add(self.sam_window.menubar)
+            self.menubar_viewport.show_all()
+
+            #Toolbar
+            children = self.toolbar_viewport.get_children()
+            self.toolbar_viewport.remove(children[0])
+            self.toolbar_viewport.add(self.sam_window.toolbar)
+            self.toolbar_viewport.show_all()
+
+        elif page_num == 2: #Regedit page
+            if self.regedit_viewport.child == None:
+                self.init_regedit_page()
+
+            #Menubar
+            children = self.menubar_viewport.get_children()
+            self.menubar_viewport.remove(children[0])
+            self.menubar_viewport.add(self.regedit_window.menubar)
+            self.menubar_viewport.show_all()
+
+            #Toolbar
+            children = self.toolbar_viewport.get_children()
+            self.toolbar_viewport.remove(children[0])
+            self.toolbar_viewport.add(self.regedit_window.toolbar)
+            self.toolbar_viewport.show_all()
+
+        elif page_num == 3: #Services page
+            if self.svcctl_viewport.child == None:
+                self.init_svcctl_page()
+
+            #Menubar
+            children = self.menubar_viewport.get_children()
+            self.menubar_viewport.remove(children[0])
+            self.menubar_viewport.add(self.svcctl_window.menubar)
+            self.menubar_viewport.show_all()
+
+            #Toolbar
+            children = self.toolbar_viewport.get_children()
+            self.toolbar_viewport.remove(children[0])
+            self.toolbar_viewport.add(self.svcctl_window.toolbar)
+            self.toolbar_viewport.show_all()
+
+        elif page_num == 4: #Crontab page
+            if self.crontab_viewport.child == None:
+                self.init_crontab_page()
+
+            #Menubar
+            children = self.menubar_viewport.get_children()
+            self.menubar_viewport.remove(children[0])
+            self.menubar_viewport.add(self.crontab_window.menubar)
+            self.menubar_viewport.show_all()
+
+            #Toolbar
+            children = self.toolbar_viewport.get_children()
+            self.toolbar_viewport.remove(children[0])
+            self.toolbar_viewport.add(self.crontab_window.toolbar)
+            self.toolbar_viewport.show_all()
+
+    def on_connect_all_button_clicked(self, widget):
+
+        if self.connection_args.has_key("connect_now") and self.connection_args["connect_now"]:
+            #if the user specified --connect-now then we probably have enough arguments to connect
+
+            if self.sam_initialized():
+                if not self.sam_window.connected():
+                    self.sam_window.on_connect_item_activate(None, **self.connection_args)
+            else:
+                self.init_sam_page()
+
+            if self.regedit_initialized():
+                if not self.regedit_window.connected():
+                    self.regedit_window.on_connect_item_activate(None, **self.connection_args)
+            else:
+                self.init_regedit_page()
+
+            if self.svcctl_initialized():
+                if not self.svcctl_window.connected():
+                    self.svcctl_window.on_connect_item_activate(None, **self.connection_args)
+            else:
+                self.init_svcctl_page()
+
+            if self.crontab_initialized():
+                if not self.crontab_window.connected():
+                    self.crontab_window.on_connect_item_activate(None, **self.connection_args)
+            else:
+                self.init_crontab_page()
+
+        else:
+            if self.run_connect_all_dialog():
+                self.on_connect_all_button_clicked(None)
+
+    def on_disconnect_all_button_clicked(self, widget):
+        if self.sam_initialized():
+            self.sam_window.on_disconnect_item_activate(None)
+        if self.regedit_initialized():
+            self.regedit_window.on_disconnect_item_activate(None)
+        if self.svcctl_initialized():
+            self.svcctl_window.on_disconnect_item_activate(None)
+        if self.crontab_initialized():
+            self.crontab_window.on_disconnect_item_activate(None)
+        self.update_sensitivity()
+
+    def on_connection_info_item_activate(self, widget):
+        #TODO: display connection info (via a dialog or custom window?)
+        self.push_status_message("This is not implemented yet!")
+        pass
+
+    def on_clear_log_activate(self, widget):
+        self.messages_textview.get_buffer().set_text("")
+
+
+    def on_about_item_activate(self, widget):
+        dialog = AboutDialog(
+                             "Main",
+                             "A tool to display other utilities in a simple, unified window.",
+                             None
+                             )
+        dialog.run()
+        dialog.hide()
+
+
+    def on_quit_item_activate(self, widget):
+        gtk.main_quit()
+
+#************ END OF CLASS ***************
+
+def PrintUseage():
+    print "Usage: %s [OPTIONS]" % (str(os.path.split(__file__)[-1]))
+    print "All options are optional. The user will be queried for additional information if needed.\n"
+    print "  -s  --server\t\tspecify the server to connect to."
+    print "  -u  --user\t\tspecify the user."
+    print "  -p  --password\tThe password for the user."
+    print "  -t  --transport\tTransport type.\n\t\t\t\t0 for RPC, SMB, TCP/IP\n\t\t\t\t1 for RPC, TCP/IP\n\t\t\t\t2 for localhost."
+    print "  -c  --connect-now\tSkip the connect dialog."
+
+def ParseArgs(argv):
+    arguments = {}
+
+    try: #get arguments into a nicer format
+        opts, args = getopt.getopt(argv, "chu:s:p:t:", ["help", "user=", "server=", "password=", "connect-now", "transport="])
+    except getopt.GetoptError:
+        PrintUseage()
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt in ("-h", "--help"):
+            PrintUseage()
+            sys.exit(0)
+        elif opt in ("-s", "--server"):
+            arguments.update({"server":arg})
+        elif opt in ("-u", "--user"):
+            arguments.update({"username":arg})
+        elif opt in ("-p", "--password"):
+            arguments.update({"password":arg})
+        elif opt in ("-t", "--transport"):
+            arguments.update({"transport_type":int(arg)})
+        elif opt in ("-c", "--connect-now"):
+            arguments.update({"connect_now":True})
+    return (arguments)
+
+
+if __name__ == "__main__":
+    arguments = ParseArgs(sys.argv[1:])
+    gtk.gdk.threads_init()
+    main_window = SambaUtilities(arguments)
+    sys.stdout = main_window #redirect print statements to the write() function of this class
+    gtk.main()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/objects.py b/build/lib.linux-x86_64-2.7/sambagtk/objects.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/pygwcrontab.py b/build/lib.linux-x86_64-2.7/sambagtk/pygwcrontab.py
new file mode 100644 (file)
index 0000000..c197e54
--- /dev/null
@@ -0,0 +1,687 @@
+#!/usr/bin/python
+
+import sys
+import os.path
+import traceback
+import getopt
+
+import gobject
+import gtk
+
+from samba import credentials
+from samba.dcerpc import atsvc
+
+from sambagtk.dialogs import (
+    AboutDialog,
+    )
+
+from sambagtk.atsvc import (
+    ATSvcConnectDialog,
+    Task,
+    TaskEditDialog,
+    )
+
+
+class ATSvcPipeManager:
+
+    def __init__(self, server_address, transport_type, username, password):
+        self.task_list = []
+
+        creds = credentials.Credentials()
+        if (username.count("\\") > 0):
+            creds.set_domain(username.split("\\")[0])
+            creds.set_username(username.split("\\")[1])
+        elif (username.count("@") > 0):
+            creds.set_domain(username.split("@")[1])
+            creds.set_username(username.split("@")[0])
+        else:
+            creds.set_domain("")
+            creds.set_username(username)
+        creds.set_workstation("")
+        creds.set_password(password)
+
+        binding = ["ncacn_np:%s", "ncacn_ip_tcp:%s", "ncalrpc:%s"][transport_type]
+
+        self.pipe = atsvc.atsvc(binding % (server_address), credentials = creds)
+
+    def close(self):
+        pass # apparently there's no .Close() method for this pipe
+
+    def fetch_tasks(self):
+        del self.task_list[:]
+
+        (ctr, total, resume) = self.pipe.JobEnum(unicode(self.pipe.server_name), atsvc.enum_ctr(), 1000000, 0)
+        if (total > 0):
+            for info in ctr.first_entry:
+                task = self.job_info_to_task(info)
+                self.task_list.append(task)
+
+    def add_task(self, task):
+        job_id = self.pipe.JobAdd(unicode(self.pipe.server_name), self.task_to_job_info(task))
+        if (job_id == 0):
+            raise RuntimeError(-1, "Invalid task information.")
+
+        task.id = job_id
+        self.task_list.append(task)
+
+    def update_task(self, task):
+        job_id = self.pipe.JobAdd(unicode(self.pipe.server_name), self.task_to_job_info(task))
+        if (job_id == 0):
+            raise Exception("invalid task information")
+
+        self.pipe.JobDel(unicode(self.pipe.server_name), task.id, task.id)
+
+        task.id = job_id
+
+    def delete_task(self, task):
+        self.pipe.JobDel(unicode(self.pipe.server_name), task.id, task.id)
+
+    def job_info_to_task(self, job_info):
+        task = Task(job_info.command, job_info.job_id)
+
+        task.job_time = job_info.job_time
+        task.days_of_month = job_info.days_of_month
+        task.days_of_week = job_info.days_of_week
+        task.run_periodically = (job_info.flags & 0x01) != 0
+        task.non_interactive = (job_info.flags & 0x10) != 0
+
+        return task
+
+    def task_to_job_info(self, task):
+        job_info = atsvc.JobInfo()
+
+        job_info.command = unicode(task.command)
+        job_info.job_time = task.job_time
+        job_info.days_of_month = task.days_of_month
+        job_info.days_of_week = task.days_of_week
+        job_info.flags = 0
+
+        if (task.run_periodically):
+            job_info.flags |= 0x01
+        if (task.non_interactive):
+            job_info.flags |= 0x10
+
+        return job_info
+
+
+class CronTabWindow(gtk.Window):
+
+    def __init__(self, info_callback=None, server="", username="", password="", transport_type=0, connect_now=False):
+        super(CronTabWindow, self).__init__()
+        # Note: Any change to these arguments should probably also be changed
+        # in on_connect_item_activate()
+
+        self.create()
+        self.pipe_manager = None
+        self.set_status("Disconnected.")
+        self.update_sensitivity()
+
+        # It's nice to have this info saved when a user wants to reconnect
+        self.server_address = server
+        self.username = username
+        self.transport_type = transport_type
+
+        self.on_connect_item_activate(None, server, transport_type, username, password, connect_now)
+
+        # This is used so the parent program can grab the server info after we've connected.
+        if info_callback is not None:
+            info_callback(server = self.server_address, username = self.username, transport_type = self.transport_type)
+
+    def create(self):
+        # main window
+
+        accel_group = gtk.AccelGroup()
+
+        self.set_title("Scheduled Tasks")
+        self.set_default_size(800, 600)
+        self.icon_filename = os.path.join(sys.path[0], "images", "crontab.png")
+        self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(self.icon_filename)
+        self.set_icon(self.icon_pixbuf)
+
+        vbox = gtk.VBox(False, 0)
+        self.add(vbox)
+
+        # menu
+
+        self.menubar = gtk.MenuBar()
+        vbox.pack_start(self.menubar, False, False, 0)
+
+        self.file_item = gtk.MenuItem("_File")
+        self.menubar.add(self.file_item)
+
+        file_menu = gtk.Menu()
+        self.file_item.set_submenu(file_menu)
+
+        self.connect_item = gtk.ImageMenuItem(gtk.STOCK_CONNECT, accel_group)
+        file_menu.add(self.connect_item)
+
+        self.disconnect_item = gtk.ImageMenuItem(gtk.STOCK_DISCONNECT, accel_group)
+        self.disconnect_item.set_sensitive(False)
+        file_menu.add(self.disconnect_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        menu_separator_item.set_sensitive(False)
+        file_menu.add(menu_separator_item)
+
+        self.quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, accel_group)
+        file_menu.add(self.quit_item)
+
+
+        self.view_item = gtk.MenuItem("_View")
+        self.menubar.add(self.view_item)
+
+        view_menu = gtk.Menu()
+        self.view_item.set_submenu(view_menu)
+
+        self.refresh_item = gtk.ImageMenuItem(gtk.STOCK_REFRESH, accel_group)
+        view_menu.add(self.refresh_item)
+
+
+        self.task_item = gtk.MenuItem("_Task")
+        self.menubar.add(self.task_item)
+
+        task_menu = gtk.Menu()
+        self.task_item.set_submenu(task_menu)
+
+        self.new_item = gtk.ImageMenuItem(gtk.STOCK_NEW, accel_group)
+        task_menu.add(self.new_item)
+
+        self.delete_item = gtk.ImageMenuItem(gtk.STOCK_DELETE, accel_group)
+        task_menu.add(self.delete_item)
+
+        self.edit_item = gtk.ImageMenuItem(gtk.STOCK_EDIT, accel_group)
+        task_menu.add(self.edit_item)
+
+
+        self.help_item = gtk.MenuItem("_Help")
+        self.menubar.add(self.help_item)
+
+        help_menu = gtk.Menu()
+        self.help_item.set_submenu(help_menu)
+
+        self.about_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT, accel_group)
+        help_menu.add(self.about_item)
+
+
+        # toolbar
+
+        self.toolbar = gtk.Toolbar()
+        vbox.pack_start(self.toolbar, False, False, 0)
+
+        self.connect_button = gtk.ToolButton(gtk.STOCK_CONNECT)
+        self.connect_button.set_is_important(True)
+        self.connect_button.set_tooltip_text("Connect to a server")
+        self.toolbar.insert(self.connect_button, 0)
+
+        self.disconnect_button = gtk.ToolButton(gtk.STOCK_DISCONNECT)
+        self.disconnect_button.set_is_important(True)
+        self.disconnect_button.set_tooltip_text("Disconnect from the server")
+        self.toolbar.insert(self.disconnect_button, 1)
+
+        self.toolbar.insert(gtk.SeparatorToolItem(), 2)
+
+        self.new_button = gtk.ToolButton(gtk.STOCK_NEW)
+        self.new_button.set_is_important(True)
+        self.toolbar.insert(self.new_button, 3)
+
+        self.edit_button = gtk.ToolButton(gtk.STOCK_EDIT)
+        self.edit_button.set_is_important(True)
+        self.toolbar.insert(self.edit_button, 4)
+
+        self.delete_button = gtk.ToolButton(gtk.STOCK_DELETE)
+        self.delete_button.set_is_important(True)
+        self.toolbar.insert(self.delete_button, 5)
+
+
+        # task list
+
+
+        self.scrolledwindow = gtk.ScrolledWindow(None, None)
+        self.scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        vbox.pack_start(self.scrolledwindow, True, True, 0)
+
+        self.tasks_tree_view = gtk.TreeView()
+        self.scrolledwindow.add(self.tasks_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("")
+        renderer = gtk.CellRendererPixbuf()
+        renderer.set_property("pixbuf", gtk.gdk.pixbuf_new_from_file_at_size(self.icon_filename, 22, 22))
+        column.pack_start(renderer, True)
+        self.tasks_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Id")
+        column.set_resizable(True)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.tasks_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Command")
+        column.set_resizable(True)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.tasks_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Schedule")
+        column.set_resizable(True)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.tasks_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        self.tasks_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        self.tasks_store.set_sort_column_id(1, gtk.SORT_ASCENDING)
+        self.tasks_tree_view.set_model(self.tasks_store)
+
+
+        # status bar
+
+        self.statusbar = gtk.Statusbar()
+        self.statusbar.set_has_resize_grip(True)
+        vbox.pack_start(self.statusbar, False, False, 0)
+
+
+        # signals/events
+
+        self.connect("delete_event", self.on_self_delete)
+        self.connect("key-press-event", self.on_key_press)
+
+        self.connect_item.connect("activate", self.on_connect_item_activate)
+        self.disconnect_item.connect("activate", self.on_disconnect_item_activate)
+        self.quit_item.connect("activate", self.on_quit_item_activate)
+        self.refresh_item.connect("activate", self.on_refresh_item_activate)
+        self.new_item.connect("activate", self.on_new_item_activate)
+        self.delete_item.connect("activate", self.on_delete_item_activate)
+        self.edit_item.connect("activate", self.on_edit_item_activate)
+        self.about_item.connect("activate", self.on_about_item_activate)
+
+        self.connect_button.connect("clicked", self.on_connect_item_activate)
+        self.disconnect_button.connect("clicked", self.on_disconnect_item_activate)
+        self.new_button.connect("clicked", self.on_new_item_activate)
+        self.delete_button.connect("clicked", self.on_delete_item_activate)
+        self.edit_button.connect("clicked", self.on_edit_item_activate)
+
+        self.tasks_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.tasks_tree_view.connect("button_press_event", self.on_tasks_tree_view_button_press)
+
+        self.add_accel_group(accel_group)
+
+    def refresh_tasks_tree_view(self):
+        if not self.connected():
+            return None
+
+        (model, paths) = self.tasks_tree_view.get_selection().get_selected_rows()
+
+        self.tasks_store.clear()
+        for task in self.pipe_manager.task_list:
+            self.tasks_store.append(task.list_view_representation())
+
+        if (len(paths) > 0):
+            self.tasks_tree_view.get_selection().select_path(paths[0])
+
+    def get_selected_task(self):
+        if not self.connected():
+            return None
+
+        (model, iter) = self.tasks_tree_view.get_selection().get_selected()
+        if (iter == None): # no selection
+            return None
+        else:
+            id = int(model.get_value(iter, 0))
+            task_list = [task for task in self.pipe_manager.task_list if task.id == id]
+            if (len(task_list) > 0):
+                return task_list[0]
+            else:
+                return None
+
+    def set_status(self, message):
+        self.statusbar.pop(0)
+        self.statusbar.push(0, message)
+
+    def update_sensitivity(self):
+        connected = (self.pipe_manager is not None)
+        selected = (self.get_selected_task() is not None)
+
+        self.connect_item.set_sensitive(not connected)
+        self.disconnect_item.set_sensitive(connected)
+        self.refresh_item.set_sensitive(connected)
+        self.new_item.set_sensitive(connected)
+        self.delete_item.set_sensitive(connected and selected)
+        self.edit_item.set_sensitive(connected and selected)
+
+        self.connect_button.set_sensitive(not connected)
+        self.disconnect_button.set_sensitive(connected)
+        self.new_button.set_sensitive(connected)
+        self.delete_button.set_sensitive(connected and selected)
+        self.edit_button.set_sensitive(connected and selected)
+
+    def run_message_dialog(self, type, buttons, message, parent = None):
+        if (parent == None):
+            parent = self
+
+        message_box = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, type, buttons, message)
+        response = message_box.run()
+        message_box.hide()
+
+        return response
+
+    def run_task_edit_dialog(self, task = None, apply_callback = None):
+        dialog = TaskEditDialog(task)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]):
+                problem_msg = dialog.check_for_problems()
+
+                if (problem_msg is not None):
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg, dialog)
+                else:
+                    dialog.values_to_task()
+                    if (apply_callback is not None):
+                        apply_callback(dialog.task)
+                    if (response_id == gtk.RESPONSE_OK):
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        return dialog.task
+
+    def run_connect_dialog(self, pipe_manager, server_address, transport_type, username, password = "", connect_now = False):
+        dialog = ATSvcConnectDialog(server_address, transport_type, username, password)
+        dialog.show_all()
+
+        # loop to handle the failures
+        while True:
+            if (connect_now):
+                connect_now = False
+                response_id = gtk.RESPONSE_OK
+            else:
+                response_id = dialog.run()
+
+            if (response_id != gtk.RESPONSE_OK):
+                dialog.hide()
+                return None
+            else:
+                try:
+                    server_address = dialog.get_server_address()
+                    self.server_address = server_address
+                    transport_type = dialog.get_transport_type()
+                    self.transport_type = transport_type
+                    username = dialog.get_username()
+                    self.username = username
+                    password = dialog.get_password()
+
+                    pipe_manager = ATSvcPipeManager(server_address, transport_type, username, password)
+                    break
+
+                except RuntimeError, re:
+                    if re.args[1] == 'Logon failure': #user got the password wrong
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Invalid username or password.", dialog)
+                        dialog.password_entry.grab_focus()
+                        dialog.password_entry.select_region(0, -1) #select all the text in the password box
+                    elif re.args[0] == 5 or re.args[1] == 'Access denied':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Access Denied.", dialog)
+                        dialog.username_entry.grab_focus()
+                        dialog.username_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_HOST_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Could not contact the server.", dialog)
+                        dialog.server_address_entry.grab_focus()
+                        dialog.server_address_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_NETWORK_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The network is unreachable.\n\nPlease check your network connection.", dialog)
+                    elif re.args[1] == 'NT_STATUS_CONNECTION_REFUSED':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The connection was refused.", dialog)
+                    else:
+                        msg = "Failed to connect: %s." % (re.args[1])
+                        print msg
+                        traceback.print_exc()
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+                except Exception, ex:
+                    msg = "Failed to connect: %s." % (str(ex))
+                    print msg
+                    traceback.print_exc()
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+        dialog.hide()
+        return pipe_manager
+
+    def connected(self):
+        return self.pipe_manager is not None
+
+    def update_task_callback(self, task):
+        try:
+            self.pipe_manager.update_task(task)
+            self.pipe_manager.fetch_tasks()
+
+            self.set_status("Task updated.")
+
+        except RuntimeError, re:
+            msg = "Failed to update task: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to update task: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_tasks_tree_view()
+
+    def on_self_delete(self, widget, event):
+        if (self.pipe_manager is not None):
+            self.on_disconnect_item_activate(self.disconnect_item)
+
+        gtk.main_quit()
+        return False
+
+    def on_key_press(self, widget, event):
+        if event.keyval == gtk.keysyms.F5:
+            #refresh when F5 is pressed
+            self.on_refresh_item_activate(None)
+        elif event.keyval == gtk.keysyms.Return:
+            myev = gtk.gdk.Event(gtk.gdk._2BUTTON_PRESS) #emulate a double-click
+            self.on_tasks_tree_view_button_press(None, myev)
+
+    def on_connect_item_activate(self, widget, server = "", transport_type = 0, username = "", password = "", connect_now = False):
+        server = server or self.server_address
+        transport_type = transport_type or self.transport_type
+        username = username or self.username
+
+        try:
+            self.pipe_manager = self.run_connect_dialog(None, server, transport_type, username, password, connect_now)
+            if (self.pipe_manager is not None):
+                self.pipe_manager.fetch_tasks()
+
+                self.set_status("Connected to %s." % (self.server_address)) #Because the global variable is updated by the run_connect_dialog() function
+
+        except RuntimeError, re:
+            msg = "Failed to retrieve the scheduled tasks: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to retrieve the scheduled tasks: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_tasks_tree_view()
+        self.update_sensitivity()
+
+    def on_disconnect_item_activate(self, widget):
+        if (self.pipe_manager is not None):
+            self.pipe_manager.close()
+            self.pipe_manager = None
+
+        self.tasks_store.clear()
+        self.update_sensitivity()
+
+        self.set_status("Disconnected.")
+
+    def on_quit_item_activate(self, widget):
+        self.on_self_delete(None, None)
+
+    def on_refresh_item_activate(self, widget):
+        try:
+            self.pipe_manager.fetch_tasks()
+
+            self.set_status("Connected to %s." % (self.server_address))
+
+        except RuntimeError, re:
+            msg = "Failed to retrieve the scheduled tasks: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to retrieve the scheduled tasks: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_tasks_tree_view()
+
+    def on_new_item_activate(self, widget):
+        new_task = self.run_task_edit_dialog()
+        if (new_task == None):
+            return
+
+        try:
+            self.pipe_manager.add_task(new_task)
+            self.pipe_manager.fetch_tasks()
+
+            self.set_status("Successfully created the task.")
+
+        except RuntimeError, re:
+            msg = "Failed to create task: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to create task: %s."  % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_tasks_tree_view()
+
+    def on_delete_item_activate(self, widget):
+        del_task = self.get_selected_task()
+
+        if (self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to delete task with ID '%d'?" % del_task.id) != gtk.RESPONSE_YES):
+            return
+
+        try:
+            self.pipe_manager.delete_task(del_task)
+            self.pipe_manager.fetch_tasks()
+
+            self.set_status("Successfully deleted the task.")
+
+        except RuntimeError, re:
+            msg = "Failed to delete task: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to delete task: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_tasks_tree_view()
+
+    def on_edit_item_activate(self, widget):
+        edit_task = self.get_selected_task()
+        self.run_task_edit_dialog(edit_task, self.update_task_callback)
+
+    def on_about_item_activate(self, widget):
+        dialog = AboutDialog(
+             "PyGWCronTab",
+             "A tool to remotely manage scheduled tasks.\n Based on Jelmer Vernooij's original Samba-GTK",
+             self.icon_pixbuf
+             )
+        dialog.run()
+        dialog.hide()
+
+    def on_tasks_tree_view_button_press(self, widget, event):
+        if (self.get_selected_task() == None):
+            return
+
+        if (event.type == gtk.gdk._2BUTTON_PRESS):
+            self.on_edit_item_activate(self.edit_item)
+
+    def on_update_sensitivity(self, widget):
+        self.update_sensitivity()
+
+
+def PrintUsage():
+    print "Usage: %s [OPTIONS]" % (str(os.path.split(__file__)[-1]))
+    print "All options are optional. The user will be queried for additional information if needed.\n"
+    print "  -s  --server\t\tspecify the server to connect to."
+    print "  -u  --user\t\tspecify the user."
+    print "  -p  --password\tThe password for the user."
+    print "  -t  --transport\tTransport type.\n\t\t\t\t0 for RPC, SMB, TCP/IP\n\t\t\t\t1 for RPC, TCP/IP\n\t\t\t\t2 for localhost."
+    print "  -c  --connect-now\tSkip the connect dialog."
+
+def ParseArgs(argv):
+    arguments = {}
+
+    try: #get arguments into a nicer format
+        opts, args = getopt.getopt(argv, "hu:s:p:ct:", ["help", "user=", "server=", "password=", "connect-now", "transport="])
+    except getopt.GetoptError:
+        PrintUsage()
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt in ("-h", "--help"):
+            PrintUsage()
+            sys.exit(0)
+        elif opt in ("-s", "--server"):
+            arguments.update({"server":arg})
+        elif opt in ("-u", "--user"):
+            arguments.update({"username":arg})
+        elif opt in ("-p", "--password"):
+            arguments.update({"password":arg})
+        elif opt in ("-t", "--transport"):
+            arguments.update({"transport_type":int(arg)})
+        elif opt in ("-c", "--connect-now"):
+            arguments.update({"connect_now":True})
+    return (arguments)
+
+if __name__ == "__main__":
+    arguments = ParseArgs(sys.argv[1:]) #the [1:] ignores the first argument, which is the path to our utility
+
+    main_window = CronTabWindow(**arguments)
+    main_window.show_all()
+    gtk.main()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/pygwregedit.py b/build/lib.linux-x86_64-2.7/sambagtk/pygwregedit.py
new file mode 100644 (file)
index 0000000..6b65e8f
--- /dev/null
@@ -0,0 +1,2162 @@
+#!/usr/bin/python
+
+import sys
+import os.path
+import traceback
+import threading
+import getopt
+
+import gobject
+import gtk
+import pango
+
+from samba import credentials
+from samba.dcerpc import winreg, security
+from samba.dcerpc import misc
+
+from sambagtk.registry import (
+    RegistryKey,
+    RegistryValue,
+    RegValueEditDialog,
+    RegKeyEditDialog,
+    RegRenameDialog,
+    RegSearchDialog,
+    RegPermissionsDialog,
+    WinRegConnectDialog,
+    )
+
+from sambagtk.dialogs import (
+    AboutDialog,
+    )
+
+
+class WinRegPipeManager(object):
+
+    def __init__(self, server_address, transport_type, username, password):
+        self.service_list = []
+        self.lock = threading.RLock()
+
+        creds = credentials.Credentials()
+        if (username.count("\\") > 0):
+            creds.set_domain(username.split("\\")[0])
+            creds.set_username(username.split("\\")[1])
+        elif (username.count("@") > 0):
+            creds.set_domain(username.split("@")[1])
+            creds.set_username(username.split("@")[0])
+        else:
+            creds.set_domain("")
+            creds.set_username(username)
+        creds.set_workstation("")
+        creds.set_password(password)
+
+        binding = ["ncacn_np:%s", "ncacn_ip_tcp:%s", "ncalrpc:%s"][transport_type]
+        self.pipe = winreg.winreg(binding % (server_address), credentials = creds)
+
+        self.open_well_known_keys()
+
+    def close(self):
+        pass # apparently there's no .Close() method for this pipe
+
+    def ls_key(self, key, regedit_window=None, progress_bar=True, confirm=True):
+        """this function gets a list of values and subkeys
+        NOTE: this function will acquire the pipe manager lock and gdk lock on its own. Do Not Acquire Either Lock Before Calling This Function!
+        \tThis means you can NOT call this function from the main thread with the regedit_window argument or you will have a deadlock. Calling without the regedit_window argument is fine.
+
+        returns (subkey_list, value_list)"""
+        subkey_list = []
+        value_list = []
+
+        update_GUI = (regedit_window is not None)
+
+        self.lock.acquire()
+        try: #this can cause access denied errors
+            path_handles = self.open_path(key)
+        except Exception as ex:
+            raise ex
+        finally:
+            self.lock.release()
+
+        key_handle = path_handles[-1]
+        blank_buff = WinRegPipeManager.winreg_string_buf("")
+
+        if (update_GUI and progress_bar):
+            num_subkeys = 4800.0 #backup value
+            self.lock.acquire()
+            try:
+                num_subkeys = float(self.pipe.QueryInfoKey(key_handle, WinRegPipeManager.winreg_string(""))[1])
+            except RuntimeError as re:
+                print "Failed to fetch key information for %s: %s." % (key.get_absolute_path(), re.args[1])
+            finally:
+                self.lock.release()
+
+        index = 0
+        while True: #get a list of subkeys
+            try:
+                self.lock.acquire()
+                (subkey_name, subkey_class, subkey_changed_time) = self.pipe.EnumKey(key_handle, index, blank_buff, blank_buff, None)
+                self.lock.release() #we want to release the pipe lock before grabbing the gdk lock or else we might cause a deadlock!
+
+                subkey = RegistryKey(subkey_name.name, key)
+                subkey_list.append(subkey)
+
+                if (update_GUI):
+                    gtk.gdk.threads_enter()
+                    regedit_window.set_status("Fetching key: %s" % (subkey_name.name))
+                    if (progress_bar):
+                        if (index < num_subkeys): #the value of total was a guess so this may cause a GtkWarning for setting fraction to a value above 1.0
+                            regedit_window.progressbar.set_fraction(index/num_subkeys)
+                            regedit_window.progressbar.show() #other threads calling ls_key() may finish and hide the progress bar.
+                    gtk.gdk.threads_leave()
+
+                index += 1
+
+            except RuntimeError as re:
+                self.lock.release()
+                if (re.args[0] == 0x103): #0x103 is WERR_NO_MORE_ITEMS, so we're done
+                    if (update_GUI and progress_bar):
+                        gtk.gdk.threads_enter()
+                        regedit_window.progressbar.hide()
+                        gtk.gdk.threads_leave()
+                    break
+                else:
+                    raise re
+
+        index = 0
+        while True: #get a list of values for the key
+            try:
+                self.lock.acquire()
+                (value_name, value_type, value_data, value_length) = self.pipe.EnumValue(key_handle,
+                                                                                         index,
+                                                                                         WinRegPipeManager.winreg_val_name_buf(""),
+                                                                                         0,
+                                                                                         [],
+                                                                                         8192
+                                                                                         )
+                self.lock.release()
+
+                value = RegistryValue(value_name.name, value_type, value_data, key)
+                value_list.append(value)
+
+                # there's no need to update GUI here since there's usually few
+                # Values. Additionally, many values are named "" which is
+                # later changed to "(Default)".  So printing '"fetching:
+                # "+value.name' might look like a glitch to the user.
+
+                index += 1
+
+            except RuntimeError as re:
+                self.lock.release()
+                if (re.args[0] == 0x103): #0x103 is WERR_NO_MORE_ITEMS
+                    break
+                else:
+                    raise re
+
+        self.lock.acquire()
+        try:
+            self.close_path(path_handles)
+        finally:
+            self.lock.release()
+
+        default_value_list = [value for value in value_list if value.name == ""]
+        if len(default_value_list) == 0:
+            value = RegistryValue("(Default)", misc.REG_SZ, [], key)
+            value_list.append(value)
+        else:
+            default_value_list[0].name = "(Default)"
+
+        if (update_GUI and confirm):
+            gtk.gdk.threads_enter()
+            regedit_window.set_status("Successfully fetched keys and values of %s." % (key.name))
+            gtk.gdk.threads_leave()
+
+#        #The reference count to Py_None is still not right: It climbs to infinity!
+#        print "Finish ls_key()", sys.getrefcount(None)
+        return (subkey_list, value_list)
+
+    def get_subkeys_for_key(self, key):
+        """this function gets a list subkeys for 'key'
+
+        returns subkey_list"""
+
+        subkey_list = []
+        path_handles = self.open_path(key)
+        key_handle = path_handles[-1]
+        blank_buff = WinRegPipeManager.winreg_string_buf("")
+        index = 0
+
+        while True: #get a list of subkeys
+            try:
+                (subkey_name,
+                 subkey_class,
+                 subkey_changed_time) = self.pipe.EnumKey(key_handle, index, blank_buff, blank_buff, None)
+
+                subkey = RegistryKey(subkey_name.name, key)
+                subkey_list.append(subkey)
+
+                index += 1
+
+            except RuntimeError as re:
+                if (re.args[0] == 0x103): #0x103 is WERR_NO_MORE_ITEMS, so we're done
+                    break
+                else:
+                    raise re
+
+        self.close_path(path_handles)
+
+        return subkey_list
+
+    def get_values_for_key(self, key):
+        """this function gets a list of values for 'key'
+
+        returns a list of values"""
+
+        value_list = []
+        path_handles = self.open_path(key)
+        key_handle = path_handles[-1]
+        index = 0
+
+        while True: #get a list of values for the key
+            try:
+                (value_name,
+                 value_type,
+                 value_data,
+                 value_length) = self.pipe.EnumValue(
+                     key_handle, index,
+                     WinRegPipeManager.winreg_val_name_buf(""), 0, [], 8192)
+
+                value = RegistryValue(value_name.name, value_type, value_data,
+                    key)
+                value_list.append(value)
+
+                index += 1
+
+            except RuntimeError as re:
+                if (re.args[0] == 0x103): #0x103 is WERR_NO_MORE_ITEMS
+                    break
+                else:
+                    raise re
+
+        self.close_path(path_handles)
+
+        # Every key is supposted to have a default value. If this key doesn't
+        # have one, we'll display a blank one
+        default_value_list = [value for value in value_list if value.name == ""]
+        if (len(default_value_list) == 0):
+            value = RegistryValue("(Default)", misc.REG_SZ, [], key)
+            value_list.append(value)
+        else:
+            default_value_list[0].name = "(Default)"
+
+        return value_list
+
+    def get_key_security(self, key):
+        #TODO: this
+
+        path_handles = self.open_path(key)
+        key_handle = path_handles[-1]
+
+        key_sec_data = winreg.KeySecurityData()
+        key_sec_data.size = 99999999 #TODO: find a better number.
+        #Fetch the DACL
+        result = self.pipe.GetKeySecurity(key_handle, security.SECINFO_DACL , key_sec_data)
+
+        #This is what Vista does. I don't know what it means...
+        vista_key_sec_data1 = self.pipe.GetKeySecurity(key_handle, 0x0e4fcce7 , key_sec_data)
+        #vista_key_sec_data2 = self.pipe.GetKeySecurity(key_handle, 0xb234a886 , key_sec_data) #this crashes, "Expected type int"
+
+
+
+        self.close_path(path_handles)
+
+        return key_sec_data
+
+
+    def create_key(self, key):
+        path_handles = self.open_path(key.parent)
+        key_handle = path_handles[len(path_handles) - 1]
+
+        (new_handle, action_taken) = self.pipe.CreateKey(
+            key_handle,
+            WinRegPipeManager.winreg_string(key.name),
+            WinRegPipeManager.winreg_string(key.name),
+            0,
+            winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE,
+            None,
+            winreg.REG_ACTION_NONE) #why this value isn't winreg.REG_CREATED_NEW_KEY is beyond me. I'm not even sure why this value is needed, what were the designers thinking?
+
+        path_handles.append(new_handle)
+
+        self.close_path(path_handles)
+
+    def move_key(self, key, old_name):
+        #TODO: implement this
+        raise NotImplementedError("Not implemented")
+
+    def remove_key(self, key):
+        """Deletes 'key' and recursively deletes all subkeys under it.
+
+        """
+        subkey_list = self.get_subkeys_for_key(key)
+
+        for subkey in subkey_list:
+            self.remove_key(subkey)
+
+        path_handles = self.open_path(key)
+        key_handle = path_handles[len(path_handles) - 2]
+
+        self.pipe.DeleteKey(key_handle, WinRegPipeManager.winreg_string(key.name))
+        self.close_path(path_handles)
+
+    def set_value(self, value):
+        path_handles = self.open_path(value.parent)
+        key_handle = path_handles[len(path_handles) - 1]
+
+        if (value.name == "(Default)"):
+            name = ""
+        else:
+            name = value.name
+
+        self.pipe.SetValue(key_handle, WinRegPipeManager.winreg_string(name), value.type, value.data)
+        self.close_path(path_handles)
+
+    def unset_value(self, value):
+        path_handles = self.open_path(value.parent)
+        key_handle = path_handles[len(path_handles) - 1]
+
+        if (value.name == "(Default)"):
+            name = ""
+        else:
+            name = value.name
+
+        self.pipe.DeleteValue(key_handle, WinRegPipeManager.winreg_string(name))
+        self.close_path(path_handles)
+
+    def move_value(self, value, old_name):
+        path_handles = self.open_path(value.parent)
+        key_handle = path_handles[len(path_handles) - 1]
+
+        self.pipe.DeleteValue(key_handle, WinRegPipeManager.winreg_string(old_name))
+        self.pipe.SetValue(key_handle, WinRegPipeManager.winreg_string(value.name), value.type, value.data)
+
+        self.close_path(path_handles)
+
+    def open_well_known_keys(self):
+        self.well_known_keys = []
+
+        #additional permissions need to be added to properly fetch security information.
+        #winreg.REG_KEY_ALL works but it's best to figure out what permission is actually needed
+
+        key_handle = self.pipe.OpenHKCR(None, winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE)
+        key = RegistryKey("HKEY_CLASSES_ROOT", None)
+        key.handle = key_handle
+        self.well_known_keys.append(key)
+
+        key_handle = self.pipe.OpenHKCU(None, winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE)
+        key = RegistryKey("HKEY_CURRENT_USER", None)
+        key.handle = key_handle
+        self.well_known_keys.append(key)
+
+        key_handle = self.pipe.OpenHKLM(None, winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE)
+        key = RegistryKey("HKEY_LOCAL_MACHINE", None)
+        key.handle = key_handle
+        self.well_known_keys.append(key)
+
+        key_handle = self.pipe.OpenHKU(None, winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE)
+        key = RegistryKey("HKEY_USERS", None)
+        key.handle = key_handle
+        self.well_known_keys.append(key)
+
+        key_handle = self.pipe.OpenHKCC(None, winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE)
+        key = RegistryKey("HKEY_CURRENT_CONFIG", None)
+        key.handle = key_handle
+        self.well_known_keys.append(key)
+
+    def open_path(self, key):
+        if (key.parent is None):
+            return [key.handle]
+        else:
+            path = self.open_path(key.parent)
+            parent_handle = path[-1]
+
+            key_handle = self.pipe.OpenKey(
+                                      parent_handle,
+                                      WinRegPipeManager.winreg_string(key.name),
+                                      0,
+                                      winreg.KEY_ENUMERATE_SUB_KEYS | winreg.KEY_CREATE_SUB_KEY | winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE
+                                      )
+
+            return path + [key_handle]
+
+    def close_path(self, path_handles):
+        for handle in path_handles[:0:-1]:
+            self.pipe.CloseKey(handle)
+
+    @staticmethod
+    def winreg_string(string):
+        ws = winreg.String()
+        ws.name = unicode(string)
+        ws.name_len = len(string)
+        ws.name_size = 8192
+
+        return ws
+
+    @staticmethod
+    def winreg_string_buf(string):
+        wsb = winreg.StringBuf()
+        wsb.name = unicode(string)
+        wsb.length = len(string)
+        wsb.size = 8192
+
+        return wsb
+
+    @staticmethod
+    def winreg_val_name_buf(string):
+        wvnb = winreg.ValNameBuf()
+        wvnb.name = unicode(string)
+        wvnb.length = len(string)
+        wvnb.size = 8192
+
+        return wvnb
+
+class KeyFetchThread(threading.Thread):
+    def __init__(self, pipe_manager, regedit_window, selected_key, iter):
+        super(KeyFetchThread, self).__init__()
+
+        self.name = "KeyFetchThread"
+        self.pipe_manager = pipe_manager
+        self.regedit_window = regedit_window
+        self.selected_key = selected_key
+        #TODO: this should take a path instead of an iter. It's possible (though not likely) that a node gets deleted
+        #      because of a refresh to it's parent key. This would invalididate the iter.
+        self.iter = iter
+
+    def run(self):
+        msg = None
+        try:
+            #the ls_key function will grab the pipe lock
+            (key_list, value_list) = self.pipe_manager.ls_key(self.selected_key, self.regedit_window)
+
+            gtk.gdk.threads_enter()
+            self.regedit_window.refresh_keys_tree_view(self.iter, key_list)
+            #self.regedit_window.keys_tree_view.get_selection().select_iter(self.iter) #select the key, in case selection has changed during fetching. This causes problems
+            #columns_autosize() already called by refresh_keys_tree_view()
+
+            self.regedit_window.refresh_values_tree_view(value_list)
+            self.regedit_window.update_sensitivity()
+            #threads_leave in the finally: section
+        except RuntimeError as re:
+            msg = "Failed to fetch information about %s: %s." % (self.selected_key.get_absolute_path(), re.args[1])
+            print msg
+
+        finally:
+            gtk.gdk.threads_leave()
+
+            if (msg is not None):
+                gtk.gdk.threads_enter()
+                self.regedit_window.set_status(msg)
+                self.regedit_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+                gtk.gdk.threads_leave()
+
+
+class SearchThread(threading.Thread):
+    def __init__(self, pipe_manager, regedit_window, options, starting_key_iter=None):
+        """This thread searches the registry using the options specified in 'options'.
+        If 'starting_key_iter' is supplied then it will search only from that key onward."""
+        super(SearchThread, self).__init__()
+
+        self.explode = False; #so we can kill this thread if we want to
+
+        self.name = "SearchThread"
+        self.pipe_manager = pipe_manager
+        self.regedit_window = regedit_window
+        self.starting_key_iter = starting_key_iter
+
+        #options are passed in a bit of a weird way.
+        (self.text,
+         self.search_keys,
+         self.search_values,
+         self.search_data,
+         self.match_whole_string) = options
+
+    def run(self):
+        if (self.match_whole_string):
+            search_items = [self.text]
+        else:
+            search_items = self.text.split()
+
+        if self.starting_key_iter is None: #Add root keys, start a normal search
+            #this will be a depth-first traversal of the key tree
+            stack = [] #we'll push keys onto this stack
+
+            self.pipe_manager.lock.acquire()
+            well_known_keys = self.pipe_manager.well_known_keys
+            self.pipe_manager.lock.release()
+
+            gtk.gdk.threads_enter()
+            for key in well_known_keys: #push the root keys onto the stack
+                stack.append((key, self.regedit_window.get_iter_for_key(key), ))
+            gtk.gdk.threads_leave()
+            stack.reverse() #we pop keys from the end of the list. Without this we'd be searching from the last root key first
+
+        else: #The user pressed find next.
+            #create the stack with only the keys we haven't searched yet
+            gtk.gdk.threads_enter() #the function below requires the GUI gui_lock because it has to get info from the gtk data structures. But i'm not positive this gui_lock is needed
+            stack = self.fill_stack()
+            gtk.gdk.threads_leave()
+
+        #stuff we need
+        model = self.regedit_window.keys_tree_view.get_model()
+        i = 999 #This is so we can print every x statements, to save CPU
+
+        #Stuff simply to speed things up
+        search_keys = self.search_keys
+        search_values = self.search_values
+        search_data = self.search_data
+        #dot operations addresses are looked up at run time, so this saves us many lookups
+        gui_lock = gtk.gdk.threads_enter
+        gui_unlock = gtk.gdk.threads_leave
+        pipe_lock = self.pipe_manager.lock.acquire
+        pipe_unlock = self.pipe_manager.lock.release
+        append_to_key_store = self.regedit_window.keys_store.append
+        set_status = self.regedit_window.set_status
+
+        while stack != []:
+            if self.explode:
+                return
+
+            (key, key_iter) = stack.pop()
+
+            #For the sake of about 8% faster search, we only display a message every few values
+            if (i >= 5):
+                i = 0
+                gui_lock()
+                set_status("Searching %s." % key.get_absolute_path())
+                gui_unlock()
+            else:
+                i += 1
+
+            #check if this key's name matches any of our search queries
+            if (search_keys):
+                for text in search_items:
+                    if (key.name.find(text) >= 0): #find() returns the index, so anything greater than -1 means found
+                        gui_lock()
+                        self.regedit_window.highlight_search_result(key_iter)
+                        msg = "Found key at: %s." % key.get_absolute_path()
+                        self.regedit_window.set_status(msg)
+                        self.regedit_window.search_thread = None
+                        gui_unlock()
+                        return
+
+            #fetch a list of values for this key
+            if (search_values or search_data):
+                pipe_lock()
+                try:
+                    value_list = self.pipe_manager.get_values_for_key(key)
+                except RuntimeError as ex:
+                    #probably a WERR_ACCESS_DENIED exception. We'll just skip over keys that can't be fetched
+                    print "Failed to fetch values for %s: %s." % (key.get_absolute_path(), ex.args[1])
+                    continue
+                finally:
+                    pipe_unlock()
+
+            #Search values' names
+            if (search_values):
+               for value in value_list: #go through every value for this key
+                   for text in search_items: #and check those values for each search string
+                        if (value.name.find(text) >= 0): #check if it's in the value's name
+                            gui_lock()
+                            self.regedit_window.refresh_values_tree_view(value_list) #Fill in the values, we'll need this to hightlight the result
+                            value_iter = self.regedit_window.get_iter_for_value(value)
+                            self.regedit_window.highlight_search_result(key_iter, value_iter)
+                            msg = "Found value at: %s." % value.get_absolute_path()
+                            self.regedit_window.set_status(msg)
+                            self.regedit_window.search_thread = None
+                            gui_unlock()
+                            return
+
+            #search values' data
+            if (search_data):
+               for value in value_list: #go through every value for this key
+                   for text in search_items: #and check those values for each search string
+                        if (value.get_data_string().find(text) >= 0): #check if it's in the value's data
+                            gui_lock()
+                            self.regedit_window.refresh_values_tree_view(value_list) #Fill in the values, we'll need this to hightlight the result
+                            value_iter = self.regedit_window.get_iter_for_value(value)
+                            self.regedit_window.highlight_search_result(key_iter, value_iter)
+                            msg = "Found data at: %s." % value.get_absolute_path()
+                            self.regedit_window.set_status(msg)
+                            self.regedit_window.search_thread = None
+                            gui_unlock()
+                            return
+
+
+            #fetch a list of subkeys for this key and append to the stack
+            append_list = []
+            gui_lock()
+            subkey_iter = model.iter_children(key_iter)
+            if subkey_iter is not None: #if the subkeys already exist in the tree view
+                while subkey_iter is not None:
+                    append_list.append((model.get_value(subkey_iter, 1), subkey_iter, ))
+                    subkey_iter = model.iter_next(subkey_iter)
+                gui_unlock()
+            else: #If we don't already have them, we have to get them
+                gui_unlock()
+                try:
+                    pipe_lock()
+                    subkey_list = self.pipe_manager.get_subkeys_for_key(key)
+                except RuntimeError as re:
+                    #probably a WERR_ACCESS_DENIED exception. We'll just skip over keys that can't be fetched
+                    print "Failed to fetch subkeys for %s: %s." % (key.get_absolute_path(), re.args[1])
+                    continue
+                finally:
+                    pipe_unlock()
+
+                #Append these keys to the parent in the TreeStore.
+                #Since we're fetching them we might as well add them to the TreeStore so that we don't have to fetch them again later
+                gui_lock()
+                #key_iter = self.regedit_window.get_iter_for_key(key)
+                for current_key in subkey_list:
+                    child_iter = append_to_key_store(key_iter, current_key.list_view_representation())
+                    append_list.append((current_key, child_iter, ))
+                gui_unlock()
+
+            append_list.reverse() #again we have to do this or else we'll search the list from bottom to top
+            stack.extend(append_list)
+
+        #if we are here then the loop has finished and found nothing
+        msg = "Search query not found."
+        if self.match_whole_string: msg += "\n\nConsider searching again with 'Match whole string' unchecked"
+        gtk.gdk.threads_enter()
+        self.regedit_window.search_thread = None
+        self.regedit_window.run_message_dialog(gtk.MESSAGE_INFO, gtk.BUTTONS_OK, msg)
+        gtk.gdk.threads_leave()
+
+    def fill_stack(self):
+        """Fills the stack with the keys we need to search. This only gets called to create the stack when the user presses 'find next'.
+        NOTE: This function requires the gdk lock. Make sure you hold the lock before calling this function."""
+        model = self.regedit_window.keys_tree_view.get_model()
+        key = model.get_value(self.starting_key_iter, 1)
+        root_key = key.get_root_key()
+        stack = []
+
+        self.pipe_manager.lock.acquire()
+        well_known_keys = self.pipe_manager.well_known_keys
+        self.pipe_manager.lock.release()
+
+        #we need to add all keys below the current root key into the stack
+        for n in range(len(well_known_keys)):
+            if well_known_keys[n].name == root_key.name: #so our root key is the nth key.
+                break
+        n += 1 #we don't want to add the current root key to the stack
+        append_list = []
+        while n < len(well_known_keys):
+            append_list.append((well_known_keys[n], self.regedit_window.get_iter_for_key(well_known_keys[n]) ,))
+            n += 1
+        append_list.reverse()
+        stack.extend(append_list)
+
+        iter_parents = [] #no parents, WOOHOO!
+        iter_current_parent = self.starting_key_iter #yes, we consider the current key a parent also
+
+        #Here we add all ancestors of self.starting_key_iter (the selected key) to key_parents and iter_parents
+        while iter_current_parent is not None:
+            #we'll add each child_iter's iter_current_parent to the key_parents list until we get to the root
+            iter_parents.append(iter_current_parent)
+            iter_current_parent = model.iter_parent(iter_current_parent)
+
+        iter_parents.reverse()
+        for parent_iter in iter_parents:
+            iter = model.iter_next(parent_iter) #This will point to the key right after the parent key of our last search result
+            append_list = []
+            while (iter is not None):
+                append_list.append((model.get_value(iter, 1), iter, ))
+                iter = model.iter_next(iter)
+            append_list.reverse()
+            stack.extend(append_list)
+
+        #Can't forget to add the starting key's children
+        key_iter = model.iter_children(self.starting_key_iter)
+        append_list = []
+        while key_iter is not None:
+            append_list.append((model.get_value(key_iter, 1), key_iter, ))
+            key_iter = model.iter_next(key_iter)
+        append_list.reverse()
+        stack.extend(append_list)
+
+        return stack
+
+    def self_destruct(self):
+        """This function will only stop the thread, it will not clean up anything or display anything to the user.
+        This way the calling thread can do it and it's guaranteed to happen right away."""
+        #this probably isn't safe. But who cares, we're killing the thread anyways
+        self.explode = True
+
+
+class RegEditWindow(gtk.Window):
+
+    def __init__(self, info_callback=None, server="", username="",
+            password="", transport_type=0, connect_now=False):
+        super(RegEditWindow, self).__init__()
+        # Note: Any change to these arguments should probably also be changed
+        # in on_connect_item_activate()
+
+        self.create()
+        self.pipe_manager = None
+        self.search_thread = None
+        self.search_last_options = None
+        self.ignore_selection_change = False
+        self.update_sensitivity()
+
+        # It's nice to have this info saved when a user wants to reconnect
+        self.server_address = server
+        self.username = username
+        self.transport_type = transport_type
+
+        self.on_connect_item_activate(None, server, transport_type, username, password, connect_now)
+
+        # This is used so the parent program can grab the server info after
+        # we've connected.
+        if info_callback is not None:
+            info_callback(server = self.server_address, username = self.username, transport_type = self.transport_type)
+
+    def create(self):
+
+        # main window
+
+        accel_group = gtk.AccelGroup()
+
+        self.set_title("Registry Editor")
+        self.set_default_size(800, 600)
+        self.icon_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.icon_registry_number_filename = os.path.join(sys.path[0], "images", "registry-number.png")
+        self.icon_registry_string_filename = os.path.join(sys.path[0], "images", "registry-string.png")
+        self.icon_registry_binary_filename = os.path.join(sys.path[0], "images", "registry-binary.png")
+        self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(self.icon_filename)
+        self.icon_registry_number_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.icon_registry_number_filename, 22, 22)
+        self.icon_registry_string_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.icon_registry_string_filename, 22, 22)
+        self.icon_registry_binary_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.icon_registry_binary_filename, 22, 22)
+
+        self.set_icon(self.icon_pixbuf)
+
+        vbox = gtk.VBox(False, 0)
+        self.add(vbox)
+
+        # TODO: assign keyboard shortcuts
+
+        # menu
+
+        self.menubar = gtk.MenuBar()
+        vbox.pack_start(self.menubar, False, False, 0)
+
+        self.file_item = gtk.MenuItem("_File")
+        self.menubar.add(self.file_item)
+
+        file_menu = gtk.Menu()
+        self.file_item.set_submenu(file_menu)
+
+        self.connect_item = gtk.ImageMenuItem(gtk.STOCK_CONNECT, accel_group)
+        file_menu.add(self.connect_item)
+
+        self.disconnect_item = gtk.ImageMenuItem(gtk.STOCK_DISCONNECT, accel_group)
+        file_menu.add(self.disconnect_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        file_menu.add(menu_separator_item)
+
+        self.import_item = gtk.MenuItem("_Import...", accel_group)
+        # TODO: implement import & export
+        #file_menu.add(self.import_item)
+
+        self.export_item = gtk.MenuItem("_Export...", accel_group)
+        #file_menu.add(self.export_item)
+
+#        menu_separator_item = gtk.SeparatorMenuItem()
+#        file_menu.add(menu_separator_item)
+
+        self.quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, accel_group)
+        file_menu.add(self.quit_item)
+
+        self.edit_item = gtk.MenuItem("_Edit")
+        self.menubar.add(self.edit_item)
+
+        self.edit_menu = gtk.Menu()
+        self.edit_item.set_submenu(self.edit_menu)
+
+        self.modify_item = gtk.ImageMenuItem("_Modify", accel_group)
+        self.edit_menu.add(self.modify_item)
+
+        self.modify_binary_item = gtk.MenuItem("Modify _Binary", accel_group)
+        self.edit_menu.add(self.modify_binary_item)
+
+        self.edit_menu.add(gtk.SeparatorMenuItem())
+
+        self.new_item = gtk.ImageMenuItem(gtk.STOCK_NEW, accel_group)
+        self.edit_menu.add(self.new_item)
+
+        new_menu = gtk.Menu()
+        self.new_item.set_submenu(new_menu)
+
+        self.new_key_item = gtk.MenuItem("_Key", accel_group)
+        new_menu.add(self.new_key_item)
+
+        new_menu.add(gtk.SeparatorMenuItem())
+
+        self.new_string_item = gtk.MenuItem("_String Value", accel_group)
+        new_menu.add(self.new_string_item)
+
+        self.new_binary_item = gtk.MenuItem("_Binary Value", accel_group)
+        new_menu.add(self.new_binary_item)
+
+        self.new_dword_item = gtk.MenuItem("_DWORD Value", accel_group)
+        new_menu.add(self.new_dword_item)
+
+        self.new_multi_string_item = gtk.MenuItem("_Multi-String Value", accel_group)
+        new_menu.add(self.new_multi_string_item)
+
+        self.new_expandable_item = gtk.MenuItem("_Expandable String Value", accel_group)
+        new_menu.add(self.new_expandable_item)
+
+        self.edit_menu.add(gtk.SeparatorMenuItem())
+
+        #TODO: Finish implementing permissions (dialogs are mostly done, just need to fetch/update the data)
+        #self.permissions_item = gtk.MenuItem("_Permissions", accel_group)
+        #self.edit_menu.add(self.permissions_item)
+
+        #self.edit_menu.add(gtk.SeparatorMenuItem())
+
+        self.delete_item = gtk.ImageMenuItem(gtk.STOCK_DELETE, accel_group)
+        self.edit_menu.add(self.delete_item)
+
+        self.rename_item = gtk.ImageMenuItem(gtk.STOCK_EDIT, accel_group)
+        self.edit_menu.add(self.rename_item)
+
+        self.edit_menu.add(gtk.SeparatorMenuItem())
+
+        self.copy_item = gtk.MenuItem("_Copy Registry Path", accel_group)
+        self.edit_menu.add(self.copy_item)
+
+        self.edit_menu.add(gtk.SeparatorMenuItem())
+
+        self.find_item = gtk.ImageMenuItem(gtk.STOCK_FIND, accel_group)
+        self.find_item.get_child().set_text("Find...")
+        self.edit_menu.add(self.find_item)
+
+        self.find_next_item = gtk.MenuItem("Find _Next", accel_group)
+        self.edit_menu.add(self.find_next_item)
+
+        self.view_item = gtk.MenuItem("_View")
+        self.menubar.add(self.view_item)
+
+        view_menu = gtk.Menu()
+        self.view_item.set_submenu(view_menu)
+
+        self.refresh_item = gtk.ImageMenuItem(gtk.STOCK_REFRESH, accel_group)
+        view_menu.add(self.refresh_item)
+
+        self.help_item = gtk.MenuItem("_Help")
+        self.menubar.add(self.help_item)
+
+        help_menu = gtk.Menu()
+        self.help_item.set_submenu(help_menu)
+
+        self.about_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT, accel_group)
+        help_menu.add(self.about_item)
+
+
+        # toolbar
+
+        self.toolbar = gtk.Toolbar()
+        vbox.pack_start(self.toolbar, False, False, 0)
+
+        self.connect_button = gtk.ToolButton(gtk.STOCK_CONNECT)
+        self.connect_button.set_is_important(True)
+        self.connect_button.set_tooltip_text("Connect to a server")
+        self.toolbar.insert(self.connect_button, 0)
+
+        self.disconnect_button = gtk.ToolButton(gtk.STOCK_DISCONNECT)
+        self.disconnect_button.set_is_important(True)
+        self.disconnect_button.set_tooltip_text("Disconnect from the server")
+        self.toolbar.insert(self.disconnect_button, 1)
+
+        self.toolbar.insert(gtk.SeparatorToolItem(), 2)
+
+        self.new_key_button = gtk.ToolButton(gtk.STOCK_NEW)
+        self.new_key_button.set_label("New Key")
+        self.new_key_button.set_tooltip_text("Create a new registry key")
+        self.new_key_button.set_is_important(True)
+        self.toolbar.insert(self.new_key_button, 3)
+
+        self.new_string_button = gtk.ToolButton(gtk.STOCK_NEW)
+        self.new_string_button.set_label("New Value")
+        self.new_string_button.set_tooltip_text("Create a new string registry value")
+        self.new_string_button.set_is_important(True)
+        self.toolbar.insert(self.new_string_button, 4)
+
+        self.rename_button = gtk.ToolButton(gtk.STOCK_EDIT)
+        self.rename_button.set_label("Rename")
+        self.rename_button.set_tooltip_text("Rename the selected key or value")
+        self.rename_button.set_is_important(True)
+        self.toolbar.insert(self.rename_button, 5)
+
+        self.delete_button = gtk.ToolButton(gtk.STOCK_DELETE)
+        self.delete_button.set_tooltip_text("Delete the selected key or value")
+        self.delete_button.set_is_important(True)
+        self.toolbar.insert(self.delete_button, 5)
+
+
+        # registry tree
+
+        # TODO: make the expanders nicely align with the icons
+
+        self.hpaned = gtk.HPaned()
+        vbox.pack_start(self.hpaned)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        scrolledwindow.set_size_request(250, 0)
+        self.hpaned.add1(scrolledwindow)
+
+        self.keys_tree_view = gtk.TreeView()
+        self.keys_tree_view.set_headers_visible(False)
+        scrolledwindow.add(self.keys_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("")
+        column.set_resizable(False)
+        renderer = gtk.CellRendererPixbuf()
+        renderer.set_property("stock-id", gtk.STOCK_DIRECTORY)
+        column.pack_start(renderer, True)
+        self.keys_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.keys_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        self.keys_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.keys_tree_view.set_model(self.keys_store)
+
+
+        # value list
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        self.hpaned.add2(scrolledwindow)
+
+        self.values_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.values_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("")
+        column.set_resizable(False)
+        renderer = gtk.CellRendererPixbuf()
+        column.pack_start(renderer, True)
+        self.values_tree_view.append_column(column)
+        column.add_attribute(renderer, "pixbuf", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_fixed_width(190)
+        #column.set_expand(True)
+        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.values_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Type")
+        column.set_resizable(True)
+        column.set_fixed_width(160)
+        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.values_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Data")
+        column.set_resizable(True)
+        column.set_fixed_width(300)
+        column.set_expand(True)
+        column.set_sort_column_id(3)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.values_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 3)
+
+        self.values_store = gtk.ListStore(gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.values_store.set_sort_column_id(1, gtk.SORT_ASCENDING)
+        self.values_tree_view.set_model(self.values_store)
+
+
+        # status bar & progress bar
+
+        self.statusbar = gtk.Statusbar()
+        self.statusbar.set_has_resize_grip(True)
+
+        self.progressbar = gtk.ProgressBar()
+        self.progressbar.set_no_show_all(True)
+        self.progressbar.hide()
+
+        hbox = gtk.HBox(False, 0)
+        hbox.pack_start(self.progressbar, False, False, 0)
+        hbox.pack_start(self.statusbar, True, True, 0)
+
+        vbox.pack_start(hbox, False, False, 0)
+
+
+        # signals/events
+
+        self.connect("delete_event", self.on_self_delete)
+        self.connect("key-press-event", self.on_key_press)
+
+        self.connect_item.connect("activate", self.on_connect_item_activate)
+        self.disconnect_item.connect("activate", self.on_disconnect_item_activate)
+        self.import_item.connect("activate", self.on_import_item_activate)
+        self.export_item.connect("activate", self.on_export_item_activate)
+        self.quit_item.connect("activate", self.on_quit_item_activate)
+        self.modify_item.connect("activate", self.on_modify_item_activate)
+        self.modify_binary_item.connect("activate", self.on_modify_binary_item_activate)
+        self.new_key_item.connect("activate", self.on_new_key_item_activate)
+        self.new_string_item.connect("activate", self.on_new_string_item_activate)
+        self.new_binary_item.connect("activate", self.on_new_binary_item_activate)
+        self.new_dword_item.connect("activate", self.on_new_dword_item_activate)
+        self.new_multi_string_item.connect("activate", self.on_new_multi_string_item_activate)
+        self.new_expandable_item.connect("activate", self.on_new_expandable_item_activate)
+        #self.permissions_item.connect("activate", self.on_permissions_item_activate)
+        self.delete_item.connect("activate", self.on_delete_item_activate)
+        self.rename_item.connect("activate", self.on_rename_item_activate)
+        self.copy_item.connect("activate", self.on_copy_item_activate)
+        self.find_item.connect("activate", self.on_find_item_activate)
+        self.find_next_item.connect("activate", self.on_find_next_item_activate)
+        self.refresh_item.connect("activate", self.on_refresh_item_activate)
+        self.about_item.connect("activate", self.on_about_item_activate)
+
+        self.connect_button.connect("clicked", self.on_connect_item_activate)
+        self.disconnect_button.connect("clicked", self.on_disconnect_item_activate)
+        self.new_key_button.connect("clicked", self.on_new_key_item_activate)
+        self.new_string_button.connect("clicked", self.on_new_string_item_activate)
+        self.delete_button.connect("clicked", self.on_delete_item_activate)
+        self.rename_button.connect("clicked", self.on_rename_item_activate)
+
+        self.keys_tree_view.get_selection().connect("changed", self.on_keys_tree_view_selection_changed)
+        self.keys_tree_view.connect("row-collapsed", self.on_keys_tree_view_row_collapsed_expanded)
+        self.keys_tree_view.connect("row-expanded", self.on_keys_tree_view_row_collapsed_expanded)
+        self.keys_tree_view.connect("button_press_event", self.on_keys_tree_view_button_press)
+        self.keys_tree_view.connect("focus-in-event", self.on_tree_views_focus_in)
+        self.values_tree_view.get_selection().connect("changed", self.on_values_tree_view_selection_changed)
+        self.values_tree_view.connect("button_press_event", self.on_values_tree_view_button_press)
+        self.values_tree_view.connect("focus-in-event", self.on_tree_views_focus_in)
+
+        self.add_accel_group(accel_group)
+
+    def refresh_keys_tree_view(self, iter, key_list, select_me_key = None):
+        """Refresh the children of 'iter' by recursively deleting all existing children and appending keys from 'key_list' as children.
+        Also selects 'select_me_key' in the tree view. 'select_me_key' is a key that is a child of the key referenced by 'iter' ('select_me_key' must be an element of 'key_list').
+
+        Returns nothing."""
+        if (not self.connected()):
+            return
+
+        (model, selected_paths) = self.keys_tree_view.get_selection().get_selected_rows()
+
+        if (iter is None):
+            #If iter is None then the tree is empty.
+            self.pipe_manager.lock.acquire()
+            well_known_keys = self.pipe_manager.well_known_keys
+            self.pipe_manager.lock.release()
+            for key in well_known_keys:
+                self.keys_store.append(None, key.list_view_representation())
+
+        else:
+            #Delete any children the selected key has
+            while (self.keys_store.iter_children(iter)):
+                self.keys_store.remove(self.keys_store.iter_children(iter))
+            #add keys from key_list as children.
+            for key in key_list:
+                self.keys_store.append(iter, key.list_view_representation())
+
+        if (iter is not None):
+            #expand the selected row
+            self.keys_tree_view.expand_row(self.keys_store.get_path(iter), False)
+
+            #Select the key select_me_key. Select_me_key is a key and not an iter, so this isn't as straight forward as it could be
+            #but we know it's a child of the key pointed to by 'iter' and an element of 'key_list.
+            if (select_me_key is not None):
+                child_iter = self.keys_store.iter_children(iter) #get the first (at index 0) child of 'iter'
+                while (child_iter is not None): #child_iter will equal none if call iter_children() or iter_next() and there is no next child.
+                    key = self.keys_store.get_value(child_iter, 1)
+                    if (key.name == select_me_key.name):
+                        self.keys_tree_view.get_selection().select_iter(child_iter) #select that key
+                        break
+                    child_iter = self.keys_store.iter_next(child_iter)
+
+            #if 'select_me_key' isn't given, then select whatever was selected before
+            elif (len(selected_paths) > 0):
+                for path in selected_paths: #there's almost certainly only one, but o well
+                    try: #try them until one works.
+                        sel_iter = self.keys_store.get_iter(path)
+                        self.keys_tree_view.get_selection().select_iter(sel_iter)
+                        break
+                    except Exception:
+                            self.keys_tree_view.get_selection().select_iter(iter) #highlight (select) 'iter'
+            else:
+                self.keys_tree_view.get_selection().select_iter(iter) #highlight (select) 'iter'
+
+        #self.keys_tree_view.columns_autosize() #This doesn't really help, it just slows down long lists
+        self.update_sensitivity()
+
+    def refresh_values_tree_view(self, value_list):
+        if (not self.connected()):
+            return
+
+        type_pixbufs = { #change misc back to winreg when the constants are in the right place
+                        misc.REG_SZ:self.icon_registry_string_pixbuf,
+                        misc.REG_EXPAND_SZ:self.icon_registry_string_pixbuf,
+                        misc.REG_BINARY:self.icon_registry_binary_pixbuf,
+                        misc.REG_DWORD:self.icon_registry_number_pixbuf,
+                        misc.REG_DWORD_BIG_ENDIAN:self.icon_registry_number_pixbuf,
+                        misc.REG_MULTI_SZ:self.icon_registry_string_pixbuf,
+                        misc.REG_QWORD:self.icon_registry_number_pixbuf,
+                        }
+
+        (model, selected_paths) = self.values_tree_view.get_selection().get_selected_rows()
+
+        self.values_store.clear()
+
+        for value in value_list:
+            try: #This can fail when we get a value of a type that isn't in type_pixbufs (such as REG_NONE)
+                self.values_store.append([type_pixbufs[value.type]] + value.list_view_representation())
+            except (KeyError, IndexError, ) as er:
+                #TODO: handle REG_NONE types better.
+                if value.type == misc.REG_NONE:
+                    print "Not displaying a hidden value at %s." % (value.get_absolute_path())
+                else:
+                    print "Failed to display %s in the value tree: values of type %s cannot be handled." % (value.get_absolute_path(), str(value.type))
+
+        if (len(selected_paths) > 0):
+            try:
+                sel_iter = self.values_store.get_iter(selected_paths[0])
+                self.values_tree_view.get_selection().select_iter(sel_iter)
+
+            except Exception:
+                if (len(value_list) > 0):
+                    last_iter = self.values_store.get_iter_first()
+                    while (self.values_store.iter_next(last_iter) is not None):
+                        last_iter = self.values_store.iter_next(last_iter)
+                    self.values_tree_view.get_selection().select_iter(last_iter)
+
+        self.update_sensitivity()
+
+    def get_selected_registry_key(self):
+        """Get the registry key that is currently selected in the tree view. Also returns the iter for that key in the tree view
+
+        Returns (iter, RegistryKey)"""
+        if not self.connected():
+            return (None, None)
+
+        (model, iter) = self.keys_tree_view.get_selection().get_selected()
+        if (iter is None): # no selection
+            return (None, None)
+        else:
+            return (iter, model.get_value(iter, 1))
+
+    def get_selected_registry_value(self):
+        """Get the registry value that is currently selected in the tree view. Also returns the iter for that value
+
+        Returns (iter, RegistryValue)"""
+        if not self.connected(): # not connected
+            return (None, None)
+
+        (model, iter) = self.values_tree_view.get_selection().get_selected()
+        if (iter is None): # no selection
+            return (None, None)
+        else:
+            return (iter, model.get_value(iter, 4))
+
+    def get_iter_for_value(self, value):
+        """This function takes a value and gets the iterator for that value in the gtk.TreeStore.
+
+        Returns an iterator or None"""
+        if not self.connected():
+            return
+
+        model = self.values_tree_view.get_model()
+        iter = model.get_iter_first()
+        while iter is not None:
+            current_value = model.get_value(iter, 4)
+            if (current_value.name == value.name):
+                return iter
+            iter = model.iter_next(iter)
+        return None
+
+    def get_iter_for_key(self, key):
+        """This function takes a key and gets the iterator for that key in the gtk.TreeStore.
+        Note: this function is SLOW. Only call this function if you cannot figure out a better method.
+
+        Returns an iterator or None"""
+        if not self.connected():
+            return
+
+        model = self.keys_tree_view.get_model()
+        path = key.get_absolute_path()
+
+        key_names = path.split("\\")
+
+        model = self.keys_tree_view.get_model()
+        current_key_iter = model.get_iter_first() #get iter to the first root node
+
+        step = 0 #currently checking this index in key_names
+        while (current_key_iter is not None):
+            current_key = model.get_value(current_key_iter, 1)
+            if current_key.name == key_names[step]:
+                if (step >= len(key_names) - 1): #if this is the last step then we've found the key! (cue audio from Zelda)
+                    return current_key_iter
+                current_key_iter = model.iter_children(current_key_iter) #step to the decendant, start itering their children (that sounds wrong...)
+                step += 1
+            else:
+                current_key_iter = model.iter_next(current_key_iter)
+
+        return None
+
+    def set_status(self, message):
+        self.statusbar.pop(0)
+        self.statusbar.push(0, message)
+
+    def update_sensitivity(self):
+        connected = self.connected()
+
+        key_selected = (self.get_selected_registry_key()[1] is not None)
+        value_selected = (self.get_selected_registry_value()[1] is not None)
+        value_set = (value_selected and len(self.get_selected_registry_value()[1].data) > 0)
+        value_default = (value_selected and self.get_selected_registry_value()[1].name == "(Default)")
+        key_focused = self.keys_tree_view.is_focus()
+        if (connected):
+            root_key_selected = (key_selected and self.get_selected_registry_key()[1].parent is None)
+
+        # sensitiviy
+
+        self.connect_item.set_sensitive(not connected)
+        self.disconnect_item.set_sensitive(connected)
+        self.import_item.set_sensitive(connected)
+        self.export_item.set_sensitive(connected)
+        self.modify_item.set_sensitive(connected and value_selected)
+        self.modify_binary_item.set_sensitive(connected and value_selected)
+        self.new_key_item.set_sensitive(connected and key_selected)
+        self.new_string_item.set_sensitive(connected and key_selected)
+        self.new_binary_item.set_sensitive(connected and key_selected)
+        self.new_dword_item.set_sensitive(connected and key_selected)
+        self.new_multi_string_item.set_sensitive(connected and key_selected)
+        self.new_expandable_item.set_sensitive(connected and key_selected)
+        #self.permissions_item.set_sensitive(connected and (key_selected or value_selected))
+        if (key_focused):
+            self.delete_item.set_sensitive(connected and key_selected and not root_key_selected)
+            self.rename_item.set_sensitive(connected and key_selected and not root_key_selected)
+        else:
+            self.delete_item.set_sensitive(connected and value_selected and (value_set or not value_default))
+            self.rename_item.set_sensitive(connected and value_selected and not value_default)
+        self.copy_item.set_sensitive(connected and key_selected)
+        self.find_item.set_sensitive(connected)
+        self.find_next_item.set_sensitive(connected)
+        self.refresh_item.set_sensitive(connected)
+
+        self.connect_button.set_sensitive(self.connect_item.state != gtk.STATE_INSENSITIVE)
+        self.disconnect_button.set_sensitive(self.disconnect_item.state != gtk.STATE_INSENSITIVE)
+        self.new_key_button.set_sensitive(self.new_key_item.state != gtk.STATE_INSENSITIVE)
+        self.new_string_button.set_sensitive(self.new_string_item.state != gtk.STATE_INSENSITIVE)
+        self.rename_button.set_sensitive(self.rename_item.state != gtk.STATE_INSENSITIVE)
+        self.delete_button.set_sensitive(self.delete_item.state != gtk.STATE_INSENSITIVE)
+
+
+        # captions
+
+        self.delete_item.get_child().set_text("Delete " + ["Value", "Key"][key_focused])
+        self.delete_button.set_tooltip_text("Delete the selected " + ["value", "key"][key_focused])
+
+        self.rename_item.get_child().set_text("Rename " + ["Value", "Key"][key_focused])
+        self.rename_button.set_tooltip_text("Rename the selected " + ["value", "key"][key_focused])
+
+    def run_message_dialog(self, type, buttons, message, parent = None):
+        if (parent is None):
+            parent = self
+
+        message_box = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, type, buttons, message)
+        response = message_box.run()
+        message_box.hide()
+
+        return response
+
+    def run_value_edit_dialog(self, value, type, apply_callback = None):
+        if (type is None):
+            type = value.type
+
+        if (value is not None):
+            original_type = value.type
+            value.type = type
+        else:
+            original_type = type
+
+
+        dialog = RegValueEditDialog(value, type)
+        dialog.show_all()
+        dialog.update_type_page_after_show()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]):
+                problem_msg = dialog.check_for_problems()
+
+                if (problem_msg is not None):
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg)
+                else:
+                    dialog.values_to_reg_value()
+                    if (apply_callback is not None):
+                        dialog.reg_value.type = original_type
+                        if (not apply_callback(dialog.reg_value)):
+                            response_id = gtk.RESPONSE_NONE
+                        dialog.reg_value.type = type
+                    if (response_id == gtk.RESPONSE_OK):
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        dialog.reg_value.type = original_type
+
+        return dialog.reg_value
+
+    def run_key_edit_dialog(self, key, apply_callback = None):
+        dialog = RegKeyEditDialog(key)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]):
+                problem_msg = dialog.check_for_problems()
+
+                if (problem_msg is not None):
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg)
+                else:
+                    dialog.values_to_reg_key()
+                    if (apply_callback is not None):
+                        if (not apply_callback(dialog.reg_key)):
+                            response_id = gtk.RESPONSE_NONE
+                    if (response_id == gtk.RESPONSE_OK):
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        return dialog.reg_key
+
+    def run_rename_dialog(self, key, value, apply_callback = None):
+        dialog = RegRenameDialog(key, value)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]):
+                problem_msg = dialog.check_for_problems()
+
+                if (problem_msg is not None):
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg)
+                else:
+                    dialog.values_to_reg()
+                    if (apply_callback is not None):
+                        if (not apply_callback([dialog.reg_value, dialog.reg_key][dialog.reg_key is not None])):
+                            response_id = gtk.RESPONSE_NONE
+                    if (response_id == gtk.RESPONSE_OK):
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        if (dialog.reg_key is None):
+            return dialog.reg_value
+        else:
+            return dialog.reg_key
+
+    def run_connect_dialog(self, pipe_manager, server_address, transport_type, username, password = "", connect_now = False):
+        dialog = WinRegConnectDialog(server_address, transport_type, username, password)
+        dialog.show_all()
+
+        # loop to handle the failures
+        while True:
+            if (connect_now):
+                connect_now = False
+                response_id = gtk.RESPONSE_OK
+            else:
+                response_id = dialog.run()
+
+            if (response_id != gtk.RESPONSE_OK):
+                dialog.hide()
+                return None
+            else:
+                try:
+                    server_address = dialog.get_server_address()
+                    self.server_address = server_address
+                    transport_type = dialog.get_transport_type()
+                    self.transport_type = transport_type
+                    username = dialog.get_username()
+                    self.username = username
+                    password = dialog.get_password()
+
+                    pipe_manager = WinRegPipeManager(server_address, transport_type, username, password)
+
+                    break
+
+                except RuntimeError, re:
+                    if re.args[1] == 'Logon failure': #user got the password wrong
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Invalid username or password.", dialog)
+                        dialog.password_entry.grab_focus()
+                        dialog.password_entry.select_region(0, -1) #select all the text in the password box
+                    elif re.args[0] == 5 or re.args[1] == 'Access denied':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Access Denied.", dialog)
+                        dialog.username_entry.grab_focus()
+                        dialog.username_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_HOST_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Could not contact the server.", dialog)
+                        dialog.server_address_entry.grab_focus()
+                        dialog.server_address_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_NETWORK_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The network is unreachable.\n\nPlease check your network connection.", dialog)
+                    elif re.args[1] == 'NT_STATUS_CONNECTION_REFUSED':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The connection was refused.", dialog)
+                    elif re.args[1] == 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: NT_STATUS_OBJECT_NAME_NOT_FOUND.\n\nIs the remote registry service running?", dialog)
+                    else:
+                        msg = "Failed to connect: %s." % (re.args[1])
+                        print msg
+                        traceback.print_exc()
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+                except Exception as ex:
+                    msg = "Failed to connect: %s." % (str(ex))
+                    print msg
+                    traceback.print_exc()
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+        dialog.hide()
+        return pipe_manager
+
+    def run_search_dialog(self):
+        dialog = RegSearchDialog()
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id == gtk.RESPONSE_OK): #the search button returns RESPONSE_OK
+                problem_msg = dialog.check_for_problems()
+                if (problem_msg is not None):
+                    self.run_message_dialog(problem_msg[1], gtk.BUTTONS_OK, problem_msg[0])
+                else:
+                    dialog.hide()
+                    break
+            else:
+                dialog.hide()
+                return
+        #this isn't very elegant, but conforms with the way other dialogs are done here
+        return (dialog.search_entry.get_text(),
+                dialog.check_match_keys.get_active(),
+                dialog.check_match_values.get_active(),
+                dialog.check_match_data.get_active(),
+                dialog.check_match_whole_string.get_active())
+
+    def connected(self):
+        return self.pipe_manager is not None
+
+    def update_value_callback(self, value):
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return False
+
+        try:
+            self.pipe_manager.lock.acquire()
+            self.pipe_manager.set_value(value)
+            value_list = self.pipe_manager.get_values_for_key(selected_key)
+
+            self.refresh_values_tree_view(value_list)
+            self.set_status("Value \'%s\' updated." % (value.get_absolute_path()))
+            return True
+        except RuntimeError, re:
+            msg = "Failed to update value: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to update value: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+        return False
+
+    def rename_key_callback(self, key):
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return False
+
+        if (key.name == key.old_name):
+            return True
+
+        #performance would be better if we released the lock between pipe manager calls, but that would make for very complex code - not worth it
+        self.pipe_manager.lock.acquire()
+        try:
+            key_list = self.pipe_manager.get_subkeys_for_key(selected_key.parent)
+
+            #check if a key with that name already exists
+            if (len([k for k in key_list if k.name == key.name]) > 0):
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "This key already exists. Please choose another name.", self)
+                return False
+
+            self.pipe_manager.move_key(key, key.old_name)
+            key_list = self.pipe_manager.get_subkeys_for_key(selected_key.parent)
+
+            key.old_name = key.name
+            parent_iter = self.keys_store.iter_parent(iter)
+            self.refresh_keys_tree_view(parent_iter, key_list, key)
+
+            self.set_status("Key \'%s\' renamed." % (key.get_absolute_path()))
+            return True
+
+        except RuntimeError, re:
+            msg = "Failed to rename key: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to rename key: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+        return False
+
+    def rename_value_callback(self, value):
+        (iter_key, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return False
+
+        (iter_value, selected_value) = self.get_selected_registry_value()
+        if (selected_value is None):
+            return False
+
+        if (value.name == value.old_name):
+            return True
+
+        self.pipe_manager.lock.acquire()
+        try:
+            value_list = self.pipe_manager.get_values_for_key(selected_key)
+
+            if (len([v for v in value_list if v.name == value.name]) > 0):
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "This value already exists. Please choose another name.", self)
+                return False
+
+            self.pipe_manager.move_value(value, value.old_name)
+            value_list = self.pipe_manager.get_values_for_key(selected_key)
+
+            value.old_name = value.name
+            self.refresh_values_tree_view(value_list)
+            self.set_status("Value \'%s\' renamed." % (value.get_absolute_path()))
+            return True
+
+        except RuntimeError, re:
+            msg = "Failed to rename value: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to rename value: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+        return False
+
+    def new_value(self, type):
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return
+
+        new_value = self.run_value_edit_dialog(None, type)
+        if (new_value is None):
+            return
+
+        new_value.parent = selected_key
+
+        self.pipe_manager.lock.acquire()
+        try:
+
+            value_list = self.pipe_manager.get_values_for_key(selected_key)
+
+            if (len([v for v in value_list if v.name == new_value.name]) > 0):
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "This value already exists.", self)
+                return False
+
+            self.pipe_manager.set_value(new_value)
+            value_list = self.pipe_manager.get_values_for_key(selected_key)
+            self.refresh_values_tree_view(value_list)
+            self.set_status("Value \'%s\' successfully added." % (new_value.get_absolute_path()))
+
+        except RuntimeError, re:
+            msg = "Failed to create value: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to create value: %s."  % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+    def highlight_search_result(self, key_iter, value_iter=None):
+        """Select key_iter in the tree store. If value_iter is not none then value_iter will also be selected.
+
+        returns True if a key was successfully selected"""
+        if not self.connected():
+            return
+        if key_iter is None:
+            return
+
+        result = False
+
+        self.ignore_selection_change = True
+        model = self.keys_tree_view.get_model()
+        try:
+            self.keys_tree_view.expand_to_path(model.get_path(key_iter))
+            self.keys_tree_view.set_cursor(model.get_path(key_iter))
+
+            result = True
+        except RuntimeError as re:
+            #this could happen when we try to highlight a value that isn't in the tree
+            print "Problem selecting key:", re.args[1]
+
+        if value_iter is not None:
+            model = self.values_tree_view.get_model()
+            try:
+                path = model.get_path(value_iter)
+                self.values_tree_view.set_cursor(path)
+                self.values_tree_view.expand_to_path(path)
+            except RuntimeError as re:
+                print "Problem selecting value:", re.args[1]
+
+        self.ignore_selection_change = False
+        return result
+
+    def on_key_press(self, widget, event):
+        if event.keyval == gtk.keysyms.F5:
+            self.on_refresh_item_activate(None)
+        elif event.keyval == gtk.keysyms.F3:
+            self.on_find_next_item_activate(None)
+        elif event.keyval == gtk.keysyms.F2:
+            self.on_rename_item_activate(None)
+        elif event.keyval == gtk.keysyms.Delete:
+            self.on_delete_item_activate(None)
+        elif event.keyval == gtk.keysyms.Return:
+            myev = gtk.gdk.Event(gtk.gdk._2BUTTON_PRESS) #emulate a double-click
+            self.on_values_tree_view_button_press(None, myev)
+#        else:
+#            print "Key pressed:", event.keyval
+
+    def on_self_delete(self, widget, event):
+        if (self.pipe_manager is not None):
+            self.on_disconnect_item_activate(self.disconnect_item)
+
+        gtk.main_quit()
+        return False
+
+    def on_connect_item_activate(self, widget, server = "", transport_type = 0, username = "", password = "", connect_now = False):
+        server = server or self.server_address
+        transport_type = transport_type or self.transport_type
+        username = username or self.username
+
+        self.pipe_manager = self.run_connect_dialog(None, server, transport_type, username, password, connect_now)
+        self.set_status("Connected to %s." % (self.server_address))
+
+        self.refresh_keys_tree_view(None, None)
+
+    def on_disconnect_item_activate(self, widget):
+        if self.search_thread is not None:
+            self.search_thread.self_destruct()
+            self.search_thread = None
+        if (self.pipe_manager is not None):
+            self.pipe_manager.close()
+            self.pipe_manager = None
+
+        self.keys_store.clear()
+        self.values_store.clear()
+        self.keys_tree_view.columns_autosize()
+        self.update_sensitivity()
+
+        self.set_status("Disconnected.")
+
+    def on_export_item_activate(self, widget):
+        pass
+
+    def on_import_item_activate(self, widget):
+        pass
+
+    def on_quit_item_activate(self, widget):
+        self.on_self_delete(None, None)
+
+    def on_modify_item_activate(self, widget):
+        if not self.connected():
+            return
+        (iter, edit_value) = self.get_selected_registry_value()
+        self.run_value_edit_dialog(edit_value, None, self.update_value_callback)
+
+    def on_modify_binary_item_activate(self, widget):
+        if not self.connected():
+            return
+        (iter, edit_value) = self.get_selected_registry_value()
+        self.run_value_edit_dialog(edit_value, misc.REG_BINARY, self.update_value_callback)
+
+        self.set_status("Value \'%s\' updated." % (edit_value.get_absolute_path()))
+
+    def on_new_key_item_activate(self, widget):
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return
+
+        new_key = self.run_key_edit_dialog(None)
+        if (new_key is None):
+            return
+
+        new_key.parent = selected_key
+
+        self.pipe_manager.lock.acquire()
+        try:
+            key_list = self.pipe_manager.get_subkeys_for_key(selected_key)
+
+            if (len([k for k in key_list if k.name == new_key.name]) > 0):
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "This key already exists.", self)
+                return False
+
+            self.pipe_manager.create_key(new_key)
+            key_list = self.pipe_manager.get_subkeys_for_key(selected_key)
+            self.refresh_keys_tree_view(iter, key_list, new_key)
+            self.set_status("Key \'%s\' successfully added." % (new_key.get_absolute_path()))
+
+        except RuntimeError, re:
+            msg = "Failed to create key: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to create key: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+    def on_new_string_item_activate(self, widget):
+        self.new_value(misc.REG_SZ)
+
+    def on_new_binary_item_activate(self, widget):
+        self.new_value(misc.REG_BINARY)
+
+    def on_new_dword_item_activate(self, widget):
+        self.new_value(misc.REG_DWORD)
+
+    def on_new_multi_string_item_activate(self, widget):
+        self.new_value(misc.REG_MULTI_SZ)
+
+    def on_new_expandable_item_activate(self, widget):
+        self.new_value(misc.REG_EXPAND_SZ)
+
+    def on_permissions_item_activate(self, widget):
+        if not self.connected():
+            return
+
+        (iter, selected_key) = self.get_selected_registry_key()
+        #fetch permissions
+        self.pipe_manager.lock.acquire()
+        try:
+            key_sec_data = self.pipe_manager.get_key_security(selected_key)
+        except RuntimeError as ex:
+            msg = "Failed to fetch permissions: %s." % (ex.args[1])
+            print msg
+#            traceback.print_exc()
+#            self.set_status(msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+
+        dialog = RegPermissionsDialog(None, None)
+        dialog.show_all()
+        pass
+
+    def on_delete_item_activate(self, widget):
+        key_focused = self.keys_tree_view.is_focus()
+
+        if (key_focused):
+            (iter, selected_key) = self.get_selected_registry_key()
+            if (selected_key is None):
+                return
+
+            if (self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to delete key '%s'?" % selected_key.name) != gtk.RESPONSE_YES):
+                return
+        else:
+            (iter, selected_value) = self.get_selected_registry_value()
+            if (selected_value is None):
+                return
+
+            if (self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to delete value '%s'?" % selected_value.name) != gtk.RESPONSE_YES):
+                return
+
+        self.pipe_manager.lock.acquire()
+        try:
+            if (key_focused):
+
+                self.pipe_manager.remove_key(selected_key)
+                key_list = self.pipe_manager.get_subkeys_for_key(selected_key.parent)
+
+                parent_iter = self.keys_store.iter_parent(iter)
+                self.refresh_keys_tree_view(parent_iter, key_list)
+
+                self.set_status("Key \'%s\' successfully deleted." % (selected_key.get_absolute_path()))
+            else:
+                self.pipe_manager.unset_value(selected_value)
+                value_list = self.pipe_manager.get_values_for_key(selected_value.parent)
+
+                self.refresh_values_tree_view(value_list)
+                self.set_status("Value \'%s\' successfully deleted." % (selected_value.get_absolute_path()))
+
+        except RuntimeError, re:
+            if re.args[1] == 'WERR_BADFILE':
+                msg = "Failed to delete value: it's already gone!"
+                self.on_refresh_item_activate(None)
+            else:
+                msg = "Failed to delete value: %s." % (re.args[1])
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to delete value: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.lock.release()
+
+    def on_rename_item_activate(self, widget):
+        if not self.connected():
+            return
+        key_focused = self.keys_tree_view.is_focus()
+
+        if (key_focused):
+            (iter, rename_key) = self.get_selected_registry_key()
+            rename_key.old_name = rename_key.name
+            self.run_rename_dialog(rename_key, None, self.rename_key_callback)
+
+        else:
+            (iter, rename_value) = self.get_selected_registry_value()
+            rename_value.old_name = rename_value.name
+            self.run_rename_dialog(None, rename_value, self.rename_value_callback)
+
+    def on_copy_item_activate(self, widget):
+        if not self.connected():
+            return
+
+        key_focused = self.keys_tree_view.is_focus()
+
+        if (key_focused):
+            (iter, selected_key) = self.get_selected_registry_key()
+            if (selected_key is None):
+                return
+
+            path = selected_key.get_absolute_path()
+        else:
+            (iter, selected_value) = self.get_selected_registry_value()
+            if (selected_value is None):
+                return
+
+            path = selected_value.get_absolute_path()
+
+        clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)
+        clipboard.set_text(path)
+
+    def on_find_item_activate(self, widget):
+        if not self.connected():
+            return
+
+        if self.search_thread is None:
+            result = self.run_search_dialog()
+            if result is None: #The user pressed cancel
+                return
+
+            self.search_last_options = result
+            self.search_thread = SearchThread(self.pipe_manager, self, result)
+            self.search_thread.start()
+        else:
+            #this means the search thread is already running!
+            msg = "A search is already under way. We can only have one search at a time.\n\nCancel the current search?"
+            response = self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, msg)
+            if response == gtk.RESPONSE_YES:
+                #we have to clean up after the thread because self_destruct() only kills the thread without any cleanup
+                self.search_thread.self_destruct()
+                self.search_thread = None
+                self.set_status("Search canceled.") #this may not get shown since the thread may not stop right away
+                self.on_find_item_activate(None) #Call this function again to get the new search started
+
+    def on_find_next_item_activate(self, widget):
+        if not self.connected():
+            return
+        if self.search_thread is not None:
+            self.run_message_dialog(gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Calm down, we're still searching!")
+            return
+        if self.search_last_options is None:
+            self.run_message_dialog(gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "There is no previous search to continue from.")
+            self.on_find_item_activate(None)
+            return
+
+        #get selections. Stuff is selected by the search, or the user can select if he/she wants to search elsewhere
+        (sel_key_iter, sel_key) = self.get_selected_registry_key()
+        (sel_value_iter, sel_value) = self.get_selected_registry_value()
+        value_model = self.values_tree_view.get_model()
+        #get search options from the last search
+        (text,
+         search_keys,
+         search_values,
+         search_data,
+         match_whole_string) = self.search_last_options
+
+        if (match_whole_string):
+            search_items = [text]
+        else:
+            search_items = text.split()
+
+        #search the remaining values in this key
+        if (search_values):
+            if sel_value_iter is not None:
+                value_iter = value_model.iter_next(sel_value_iter) #point to the value after the one we just found
+            else:
+                value_iter = value_model.get_iter_first()
+            while (value_iter is not None): #this will be none when there is no more values
+                current_value = value_model.get_value(value_iter, 4)
+                for text in search_items:
+                    if (current_value.name.find(text) >= 0): #check if it's in the value's name
+                        self.highlight_search_result(sel_key_iter, value_iter)
+                        msg = "Found value at: %s." % (current_value.get_absolute_path())
+                        self.set_status(msg)
+                        return
+                value_iter = value_model.iter_next(value_iter)
+
+        #search the remaining data too
+        if (search_data):
+            if sel_value_iter is not None:
+                value_iter = value_model.iter_next(sel_value_iter) #point to the value after the one we just found
+            else:
+                value_iter = value_model.get_iter_first()
+            while (value_iter is not None):
+                current_value = value_model.get_value(value_iter, 4)
+                for text in search_items: #and check those values for each search string
+                    if (current_value.get_data_string().find(text) >= 0): #check if it's in the value's data
+                        self.highlight_search_result(sel_key_iter, value_iter)
+                        msg = "Found data at: %s." % (current_value.get_absolute_path())
+                        self.set_status(msg)
+                        return
+                value_iter = value_model.iter_next(value_iter)
+
+        #so it's not in this key's values. Lets continue searching the rest of the registry
+        self.search_thread = SearchThread(self.pipe_manager, self, self.search_last_options, sel_key_iter)
+        self.search_thread.start()
+
+    def on_refresh_item_activate(self, widget):
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return
+
+        KeyFetchThread(self.pipe_manager, self, selected_key, iter).start()
+
+        #deselect any selected values
+        (iter, value) = self.get_selected_registry_value()
+        if iter is None:
+            return
+        selector = self.values_tree_view.get_selection()
+        selector.unselect_iter(iter)
+
+    def on_about_item_activate(self, widget):
+        dialog = AboutDialog(
+                             "PyGWRegEdit",
+                             "A tool to remotely edit a Windows Registry.\n Based on Jelmer Vernooij's original Samba-GTK",
+                             self.icon_pixbuf
+                             )
+        dialog.run()
+        dialog.hide()
+
+    def on_keys_tree_view_selection_changed(self, widget):
+        if self.ignore_selection_change:
+            return
+        (iter, selected_key) = self.get_selected_registry_key()
+        if (selected_key is None):
+            return
+
+        self.set_status("Selected path \'%s\'." % (selected_key.get_absolute_path()))
+
+        #deselect any selected values
+        (val_iter, value) = self.get_selected_registry_value()
+        if val_iter is not None:
+            selector = self.values_tree_view.get_selection()
+            selector.unselect_iter(val_iter)
+
+        #If this key has children already then we don't need to fetch it again.
+        #this means that keys without subkeys will always be fetched when clicked.
+        #This is a minor flaw because fetching zero keys is fast
+        child_count = self.keys_store.iter_n_children(iter)
+        if (child_count == 0):
+            #create a thread to fetch the keys.
+            KeyFetchThread(self.pipe_manager, self, selected_key, iter).start()
+        else:
+            self.pipe_manager.lock.acquire()
+            try:
+                value_list = self.pipe_manager.get_values_for_key(selected_key)
+                self.refresh_values_tree_view(value_list)
+            except Exception, ex:
+                msg = "Failed to get values for %s: %s." % (selected_key.get_absolute_path(), str(ex))
+                print msg
+                self.set_status(msg)
+            finally:
+                self.pipe_manager.lock.release()
+
+    def on_keys_tree_view_row_collapsed_expanded(self, widget, iter, path):
+        self.keys_tree_view.columns_autosize()
+
+    def on_keys_tree_view_button_press(self, widget, event):
+        if (event.type == gtk.gdk._2BUTTON_PRESS): #double click
+            (iter, selected_key) = self.get_selected_registry_key()
+            if (selected_key is None):
+                return
+
+            expanded = self.keys_tree_view.row_expanded(self.keys_store.get_path(iter))
+
+            if (expanded):
+                self.keys_tree_view.collapse_row(self.keys_store.get_path(iter))
+            else:
+                self.keys_tree_view.expand_row(self.keys_store.get_path(iter), False)
+        elif (event.button == 3): #right click
+            self.values_tree_view.grab_focus()
+            self.edit_menu.popup(None, None, None, event.button, int(event.time))
+
+    def on_values_tree_view_selection_changed(self, widget):
+        if self.ignore_selection_change:
+            return
+        (iter, selected_value) = self.get_selected_registry_value()
+
+        if (selected_value is not None):
+            self.set_status("Selected path \'%s\\%s\'." % (selected_value.get_absolute_path(), selected_value.name))
+
+        self.update_sensitivity()
+
+    def on_values_tree_view_button_press(self, widget, event):
+        if (event.type == gtk.gdk._2BUTTON_PRESS): #double click
+            (iter, selected_value) = self.get_selected_registry_value()
+            if (selected_value is None):
+                return
+
+            self.on_modify_item_activate(self.modify_item)
+        elif (event.button == 3): #right click
+            self.values_tree_view.grab_focus()
+            self.edit_menu.popup(None, None, None, event.button, int(event.time))
+
+    def on_tree_views_focus_in(self, widget, event):
+        self.update_sensitivity()
+
+
+def PrintUsage():
+    print "Usage: %s [OPTIONS]" % (str(os.path.split(__file__)[-1]))
+    print "All options are optional. The user will be queried for additional information if needed.\n"
+    print "  -s  --server\t\tspecify the server to connect to."
+    print "  -u  --user\t\tspecify the user."
+    print "  -p  --password\tThe password for the user."
+    print "  -t  --transport\tTransport type.\n\t\t\t\t0 for RPC, SMB, TCP/IP\n\t\t\t\t1 for RPC, TCP/IP\n\t\t\t\t2 for localhost."
+    print "  -c  --connect-now\tSkip the connect dialog."
+
+def ParseArgs(argv):
+    arguments = {}
+
+    try: #get arguments into a nicer format
+        opts, args = getopt.getopt(argv, "chu:s:p:t:", ["help", "user=", "server=", "password=", "connect-now", "transport="])
+    except getopt.GetoptError:
+        PrintUsage()
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt in ("-h", "--help"):
+            PrintUsage()
+            sys.exit(0)
+        elif opt in ("-s", "--server"):
+            arguments.update({"server":arg})
+        elif opt in ("-u", "--user"):
+            arguments.update({"username":arg})
+        elif opt in ("-p", "--password"):
+            arguments.update({"password":arg})
+        elif opt in ("-t", "--transport"):
+            arguments.update({"transport_type":int(arg)})
+        elif opt in ("-c", "--connect-now"):
+            arguments.update({"connect_now":True})
+    return (arguments)
+
+"""
+    Info about the thread locks used in this utility:
+the pipe lock is <pipe manager instance>.lock.acquire() and .release()
+the gdk lock (main thread lock) is simply gtk.gdk.threads_enter() and .threads_leave(), no need to get an instance
+the gdk lock is automatically acquired and released with each iteration of the gtk.main() loop.
+    So that means every time a callback function is called in the main thread (for example on_connect_item_activate()),
+    it will automatically grab the lock, run the function, and release it afterwards
+If you have to, you may acquire both locks at the same time as long as you get the gdk lock first!
+"""
+
+if __name__ == "__main__":
+    arguments = ParseArgs(sys.argv[1:]) #the [1:] ignores the first argument, which is the path to our utility
+
+    gtk.gdk.threads_init()
+    window = RegEditWindow(**arguments)
+    window.show_all()
+    gtk.main()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/pygwsam.py b/build/lib.linux-x86_64-2.7/sambagtk/pygwsam.py
new file mode 100644 (file)
index 0000000..137d987
--- /dev/null
@@ -0,0 +1,1327 @@
+#!/usr/bin/python
+
+import sys
+import os.path
+import traceback
+import getopt
+import gobject
+import gtk
+
+from samba import credentials
+from samba.dcerpc import (
+    samr,
+    security,
+    lsa,
+    )
+
+from sambagtk.sam import (
+    User,
+    Group,
+    UserEditDialog,
+    GroupEditDialog,
+    SAMConnectDialog,
+    )
+
+from sambagtk.dialogs import (
+    AboutDialog,
+    )
+
+class SAMPipeManager(object):
+
+    def __init__(self, server_address, transport_type, username, password):
+        self.user_list = []
+        self.group_list = []
+
+        creds = credentials.Credentials()
+        if (username.count("\\") > 0):
+            creds.set_domain(username.split("\\")[0])
+            creds.set_username(username.split("\\")[1])
+        elif (username.count("@") > 0):
+            creds.set_domain(username.split("@")[1])
+            creds.set_username(username.split("@")[0])
+        else:
+            creds.set_domain("")
+            creds.set_username(username)
+        creds.set_workstation("")
+        creds.set_password(password)
+
+        binding = ["ncacn_np:%s", "ncacn_ip_tcp:%s", "ncalrpc:%s"][transport_type]
+
+        self.pipe = samr.samr(binding % (server_address), credentials = creds)
+        connection_info = samr.ConnectInfo1()
+        self.connect_handle = self.pipe.Connect5(None,
+                                                 0x00000030, #SAMR_SERVER_ACCESS_ENUM_DOMAINS | SAMR_SERVER_ACCESS_OPEN_DOMAIN. Copied from a Windows Vista machine. I couldn't find the correct constants in samr or security
+                                                 1,
+                                                 connection_info)[2] #Note the [2]!!
+
+    def close(self):
+        if (self.pipe is not None):
+            self.pipe.Close(self.connect_handle)
+
+    def fetch_and_get_domain_names(self):
+        if (self.pipe is None): # not connected
+            return None
+
+        domain_name_list = []
+
+        self.sam_domains = self.toArray(self.pipe.EnumDomains(self.connect_handle, 0, -1))
+        for (rid, domain_name) in self.sam_domains:
+            domain_name_list.append(self.get_lsa_string(domain_name))
+
+        return domain_name_list
+
+    def set_current_domain(self, domain_index):
+        self.domain = self.sam_domains[domain_index]
+
+        self.domain_sid = self.pipe.LookupDomain(self.connect_handle, self.domain[1])
+        self.domain_handle = self.pipe.OpenDomain(self.connect_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
+
+    def fetch_users_and_groups(self):
+        del self.user_list[:]
+        del self.group_list[:]
+
+        # fetch groups
+        #TODO: this section may not fetch all info that it should
+        self.sam_groups = self.toArray(self.pipe.EnumDomainGroups(self.domain_handle, 0, -1))
+
+        for (rid, groupname) in self.sam_groups:
+            group = self.fetch_group(rid)
+            self.group_list.append(group)
+
+
+        # fetch users
+        self.sam_users = self.toArray(self.pipe.EnumDomainUsers(self.domain_handle, 0, 0, -1))
+
+        for (rid, username) in self.sam_users:
+            user = self.fetch_user(rid)
+            self.user_list.append(user)
+
+    def add_user(self, user):
+        """Creates 'user' on the remote computer.
+
+        This function will update user's RID.
+
+        :return: 'user' with updated RID
+        """
+
+        #Creates the new user on the server using default values for everything. Only the username is taken into account here.
+        (user_handle, rid) = self.pipe.CreateUser(self.domain_handle, self.set_lsa_string(user.username), security.SEC_FLAG_MAXIMUM_ALLOWED)
+        new_user = self.fetch_user(rid)
+
+        user.rid = rid #update the user's RID
+        if user.group_list == []: #The user must be part of a group. If the user is not part of any groups, the user is actually part of the "None" group!
+            user.group_list = new_user.group_list #use the default values assigned to the user when it was created on the server, which is probably "None"
+
+        self.update_user(user) #send the other user information to the server.
+        user = self.fetch_user(rid, user) # just to make sure we have the updated user properties
+        self.user_list.append(user)
+
+        return user
+
+    def add_group(self, group):
+        (group_handle, rid) = self.pipe.CreateDomainGroup(self.domain_handle, self.set_lsa_string(group.name), security.SEC_FLAG_MAXIMUM_ALLOWED)
+        group.rid = rid
+        group = self.fetch_group(rid, group)
+
+        self.update_group(group)
+        group = self.fetch_group(rid, group) # just to make sure we have the updated group properties
+
+
+        self.group_list.append(group)
+
+    def update_user(self, user):
+        """Submit any changes to 'user' to the server.
+        
+        The User's RID must be correct for this to work.
+        This function will call update_user_security() to update user security
+        options.
+        """
+        user_handle = self.pipe.OpenUser(self.domain_handle,
+            security.SEC_FLAG_MAXIMUM_ALLOWED, user.rid)
+
+        #Note: Most of pipe manager calls in this function could be replaced by
+        #      one call to QueryUserInfo() and SetUserInfo() using level 21
+        #      (samr.UserAllInformation)
+        #    Don't let this confuse you, it's essentially the same thing.
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserNameInformation)
+        # info.account_name = self.set_lsa_string(user.username) #Account name
+        # should never be changed.
+        info.full_name = self.set_lsa_string(user.fullname)
+        self.pipe.SetUserInfo(user_handle, samr.UserNameInformation, info)
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserAdminCommentInformation)
+        info.description = self.set_lsa_string(user.description)
+        self.pipe.SetUserInfo(user_handle, samr.UserAdminCommentInformation, info)
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserControlInformation)
+        if (user.must_change_password):
+            info.acct_flags |= samr.ACB_PW_EXPIRED
+        else:
+            info.acct_flags &= ~samr.ACB_PW_EXPIRED
+
+        if (user.password_never_expires):
+            info.acct_flags |= samr.ACB_PWNOEXP
+        else:
+            info.acct_flags &= ~samr.ACB_PWNOEXP
+
+        if (user.account_disabled):
+            info.acct_flags |= samr.ACB_DISABLED
+        else:
+            info.acct_flags &= ~samr.ACB_DISABLED
+
+        if (user.account_locked_out):
+            info.acct_flags |= samr.ACB_AUTOLOCK
+        else:
+            info.acct_flags &= ~samr.ACB_AUTOLOCK
+        self.pipe.SetUserInfo(user_handle, samr.UserControlInformation, info)
+
+        #User cannot change password is updated in the security function
+        self.update_user_security(user_handle, user)
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserProfileInformation)
+        info.profile_path = self.set_lsa_string(user.profile_path)
+        self.pipe.SetUserInfo(user_handle, samr.UserProfileInformation, info)
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserScriptInformation)
+        info.logon_script = self.set_lsa_string(user.logon_script)
+        self.pipe.SetUserInfo(user_handle, samr.UserScriptInformation, info)
+
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserHomeInformation)
+        info.home_directory = self.set_lsa_string(user.homedir_path)
+
+        if (user.map_homedir_drive == -1):
+            info.home_drive = self.set_lsa_string("")
+        else:
+            info.home_drive = self.set_lsa_string(chr(user.map_homedir_drive + ord('A')) + ":")
+        self.pipe.SetUserInfo(user_handle, samr.UserHomeInformation, info)
+
+        # get the user's old groups list
+        group_list = self.rwa_list_to_group_list(self.pipe.GetGroupsForUser(user_handle).rids)
+
+        # The user must be part of a group. If the user is not part of any
+        # groups, the user is actually part of the "None" group!
+        if (user.group_list == []):
+            user.group_list = [grp for grp in self.group_list if grp.name == unicode("None")] #if grp.name == unicode("None")
+
+        # remove the user from groups
+        for group in group_list:
+            if (user.group_list.count(group) == 0):
+                group_handle = self.pipe.OpenGroup(self.domain_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, group.rid)
+                self.pipe.DeleteGroupMember(group_handle, user.rid)
+
+        # add the user to groups
+        for group in user.group_list:
+            if (group_list.count(group) == 0):
+                group_handle = self.pipe.OpenGroup(self.domain_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, group.rid)
+                self.pipe.AddGroupMember(group_handle, user.rid, samr.SE_GROUP_ENABLED)
+
+    def update_user_security(self, user_handle, user):
+        """Updates the access mask for 'user'.
+
+        returns nothing"""
+        secinfo = self.pipe.QuerySecurity(user_handle, security.SECINFO_DACL)
+        sid = str(self.pipe.RidToSid(self.domain_handle, user.rid))
+
+        # this is for readability, we could just do
+        # secinfo.sd.dacl.aces[i].trustee if we wanted
+        security_descriptor = secinfo.sd
+        DACL = security_descriptor.dacl
+        ace_list = DACL.aces
+
+        ace = None
+
+        for item in ace_list:
+            if str(item.trustee) == sid:
+                ace = item
+                break
+
+        if ace is None:
+            print "unable to fetch security info for", user.username, "because none exists."
+            return user
+
+        if user.cannot_change_password:
+            ace_list[0].access_mask &= ~samr.SAMR_USER_ACCESS_CHANGE_PASSWORD
+            ace.access_mask &= ~samr.SAMR_USER_ACCESS_CHANGE_PASSWORD
+        else:
+            ace_list[0].access_mask |= samr.SAMR_USER_ACCESS_CHANGE_PASSWORD
+            ace.access_mask |= samr.SAMR_USER_ACCESS_CHANGE_PASSWORD
+
+        self.pipe.SetSecurity(user_handle, security.SECINFO_DACL, secinfo)
+        return
+
+    def update_group(self, group):
+        group_handle = self.pipe.OpenGroup(self.domain_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, group.rid)
+
+        info = self.set_lsa_string(group.name)
+        self.pipe.SetGroupInfo(group_handle, 2, info)
+
+        info = self.set_lsa_string(group.description)
+        self.pipe.SetGroupInfo(group_handle, 4, info)
+
+    def delete_user(self, user):
+        user_handle = self.pipe.OpenUser(self.domain_handle,
+            security.SEC_FLAG_MAXIMUM_ALLOWED, user.rid)
+        self.pipe.DeleteUser(user_handle)
+
+    def delete_group(self, group):
+        group_handle = self.pipe.OpenGroup(self.domain_handle,
+            security.SEC_FLAG_MAXIMUM_ALLOWED, group.rid)
+        self.pipe.DeleteDomainGroup(group_handle)
+
+    def fetch_user(self, rid, user=None):
+        """Fetch the User whose RID is 'rid'. A new User structure is created if the 'user' argument is left out.
+
+        Returns a User"""
+        user_handle = self.pipe.OpenUser(self.domain_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
+
+        # this handles most of the information we need
+        info = self.pipe.QueryUserInfo(user_handle, samr.UserAllInformation)
+        user = self.info_to_user(info, user)
+
+        # some settings, such as "user cannot change password", are actually
+        # part of an access list (ACL)
+        secinfo = self.pipe.QuerySecurity(user_handle, security.SECINFO_DACL)
+        user = self.secinfo_to_user(secinfo, user)
+
+
+        group_rwa_list = self.pipe.GetGroupsForUser(user_handle).rids
+        user.group_list = self.rwa_list_to_group_list(group_rwa_list)
+
+        return user
+
+    def fetch_group(self, rid, group=None):
+        group_handle = self.pipe.OpenGroup(self.domain_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
+        info = self.pipe.QueryGroupInfo(group_handle, 1)
+        group = self.info_to_group(info, group)
+        group.rid = rid
+
+        return group
+
+    def info_to_user(self, query_info, user=None):
+        """Converts 'query_info' information into a user type.
+        
+        Values in 'user' will be overwriten by this function. If called with 'None' then a new User structure will be created
+
+        returns 'user
+        '"""
+        if user is None:
+            user = User(self.get_lsa_string(query_info.account_name),
+                        self.get_lsa_string(query_info.full_name),
+                        self.get_lsa_string(query_info.description),
+                        query_info.rid)
+        else:
+            user.username = self.get_lsa_string(query_info.account_name)
+            user.full_name = self.get_lsa_string(query_info.full_name)
+            user.description = self.get_lsa_string(query_info.description)
+            user.rid = query_info.rid
+
+        user.must_change_password = (query_info.acct_flags & samr.ACB_PW_EXPIRED) != 0
+        user.password_never_expires = (query_info.acct_flags & samr.ACB_PWNOEXP) != 0
+        user.account_disabled = (query_info.acct_flags & samr.ACB_DISABLED) != 0
+        user.account_locked_out = (query_info.acct_flags & samr.ACB_AUTOLOCK) != 0
+        #cannot_change_password doesn't get set in a flag, it's a little different
+        user.profile_path = self.get_lsa_string(query_info.profile_path)
+        user.logon_script = self.get_lsa_string(query_info.logon_script)
+        user.homedir_path = self.get_lsa_string(query_info.home_directory)
+
+        drive = self.get_lsa_string(query_info.home_drive)
+        if (len(drive) == 2):
+            user.map_homedir_drive = ord(drive[0]) - ord('A')
+        else:
+            user.map_homedir_drive = -1
+
+        return user
+
+    def secinfo_to_user(self, secinfo, user):
+        """Takes 'secinfo' and updates the related fields in 'user'
+
+        returns updated 'user'"""
+        # this is for readability, we could just do secinfo.sd.dacl.aces[0] if
+        # we wanted
+        security_descriptor = secinfo.sd
+        DACL = security_descriptor.dacl
+        ace_list = DACL.aces
+
+        # we don't really need to find the user in ace_list because the first
+        # entry (S-1-1-0) should have the same flags anyways
+        ace =  ace_list[0]
+        user.cannot_change_password = (samr.SAMR_USER_ACCESS_CHANGE_PASSWORD & ace.access_mask) == 0
+
+        return user
+
+    def rwa_list_to_group_list(self, rwa_list):
+        group_list = []
+
+        for rwa in rwa_list:
+            group_rid = rwa.rid
+            group_to_add = None
+
+            for group in self.group_list:
+                if group.rid == group_rid:
+                    group_to_add = group
+                    break
+
+            if group_to_add is not None:
+                group_list.append(group_to_add)
+            else:
+                raise Exception("group not found for rid = %d" % group_rid)
+
+        return group_list
+
+    def info_to_group(self, query_info, group=None):
+        if group is None:
+            group = Group(self.get_lsa_string(query_info.name),
+                          self.get_lsa_string(query_info.description),
+                          0)
+        else:
+            group.name = self.get_lsa_string(query_info.name)
+            group.description = self.get_lsa_string(query_info.description)
+
+        return group
+
+    @staticmethod
+    def toArray((handle, array, num_entries)):
+        ret = []
+        for x in range(num_entries):
+            ret.append((array.entries[x].idx, array.entries[x].name))
+        return ret
+
+    @staticmethod
+    def get_lsa_string(str):
+        return str.string
+
+    @staticmethod
+    def set_lsa_string(str):
+        lsa_string = lsa.String()
+        lsa_string.string = unicode(str)
+        lsa_string.length = len(str)
+        lsa_string.size = len(str)
+        return lsa_string
+
+
+class SAMWindow(gtk.Window):
+
+    def __init__(self, info_callback=None, server="", username="", password="",
+            transport_type=0, domain_index=0, connect_now=False):
+        super(SAMWindow, self).__init__()
+        # Note: Any change to these arguments should probably also be changed
+        # in on_connect_item_activate()
+
+        self.create()
+        self.pipe_manager = None
+        self.users_groups_notebook_page_num = 0
+        self.update_captions()
+        self.update_sensitivity()
+
+        # It's nice to have this info saved when a user wants to reconnect
+        self.server_address = server
+        self.username = username
+        self.transport_type = transport_type
+
+        self.set_status("Disconnected.")
+        self.on_connect_item_activate(None, server, transport_type, username, password, connect_now, domain_index)
+
+        # This is used so the parent program can grab the server info after
+        # we've connected.
+        if info_callback is not None:
+            info_callback(server=self.server_address, username=self.username,
+                    transport_type=self.transport_type)
+
+    def create(self):
+        # main window
+        accel_group = gtk.AccelGroup()
+
+        self.set_title("User/Group Management")
+        self.set_default_size(800, 600)
+        self.icon_filename = os.path.join(sys.path[0], "images", "group.png")
+        self.user_icon_filename = os.path.join(sys.path[0], "images", "user.png")
+        self.group_icon_filename = os.path.join(sys.path[0], "images", "group.png")
+        self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(self.icon_filename)
+        self.set_icon(self.icon_pixbuf)
+
+        vbox = gtk.VBox(False, 0)
+        self.add(vbox)
+
+        # menu
+        self.menubar = gtk.MenuBar()
+        vbox.pack_start(self.menubar, False, False, 0)
+
+        self.file_item = gtk.MenuItem("_File")
+        self.menubar.add(self.file_item)
+
+        file_menu = gtk.Menu()
+        self.file_item.set_submenu(file_menu)
+
+        self.connect_item = gtk.ImageMenuItem(gtk.STOCK_CONNECT, accel_group)
+        file_menu.add(self.connect_item)
+
+        self.disconnect_item = gtk.ImageMenuItem(gtk.STOCK_DISCONNECT, accel_group)
+        self.disconnect_item.set_sensitive(False)
+        file_menu.add(self.disconnect_item)
+
+        self.sel_domain_item = gtk.MenuItem("_Select Domain", accel_group)
+        self.sel_domain_item.set_sensitive(False)
+        file_menu.add(self.sel_domain_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        menu_separator_item.set_sensitive(False)
+        file_menu.add(menu_separator_item)
+
+        self.quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, accel_group)
+        file_menu.add(self.quit_item)
+
+
+        self.view_item = gtk.MenuItem("_View")
+        self.menubar.add(self.view_item)
+
+        view_menu = gtk.Menu()
+        self.view_item.set_submenu(view_menu)
+
+        self.refresh_item = gtk.ImageMenuItem(gtk.STOCK_REFRESH, accel_group)
+        self.refresh_item.set_sensitive(False)
+        view_menu.add(self.refresh_item)
+
+
+        self.user_group_item = gtk.MenuItem("_User")
+        self.menubar.add(self.user_group_item)
+
+        user_group_menu = gtk.Menu()
+        self.user_group_item.set_submenu(user_group_menu)
+
+        self.new_item = gtk.ImageMenuItem(gtk.STOCK_NEW, accel_group)
+        self.new_item.set_sensitive(False)
+        user_group_menu.add(self.new_item)
+
+        self.delete_item = gtk.ImageMenuItem(gtk.STOCK_DELETE, accel_group)
+        self.delete_item.set_sensitive(False)
+        user_group_menu.add(self.delete_item)
+
+        self.edit_item = gtk.ImageMenuItem(gtk.STOCK_EDIT, accel_group)
+        self.edit_item.set_sensitive(False)
+        user_group_menu.add(self.edit_item)
+
+
+        self.policies_item = gtk.MenuItem("_Policies")
+        # self.menubar.add(self.policies_item) TODO: implement policies functionality
+
+        policies_menu = gtk.Menu()
+        self.policies_item.set_submenu(policies_menu)
+
+        self.user_rights_item = gtk.MenuItem("_User Rights...", accel_group)
+        self.user_rights_item.set_sensitive(False)
+        policies_menu.add(self.user_rights_item)
+
+        self.audit_item = gtk.MenuItem("A_udit...", accel_group)
+        self.audit_item.set_sensitive(False)
+        policies_menu.add(self.audit_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        menu_separator_item.set_sensitive(False)
+        policies_menu.add(menu_separator_item)
+
+        self.trust_relations_item = gtk.MenuItem("_Trust relations", accel_group)
+        self.trust_relations_item.set_sensitive(False)
+        policies_menu.add(self.trust_relations_item)
+
+
+        self.help_item = gtk.MenuItem("_Help")
+        self.menubar.add(self.help_item)
+
+        help_menu = gtk.Menu()
+        self.help_item.set_submenu(help_menu)
+
+        self.about_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT, accel_group)
+        help_menu.add(self.about_item)
+
+        # toolbar
+        self.toolbar = gtk.Toolbar()
+        vbox.pack_start(self.toolbar, False, False, 0)
+
+        self.connect_button = gtk.ToolButton(gtk.STOCK_CONNECT)
+        self.connect_button.set_is_important(True)
+        self.connect_button.set_tooltip_text("Connect to a server")
+        self.toolbar.insert(self.connect_button, 0)
+
+        self.disconnect_button = gtk.ToolButton(gtk.STOCK_DISCONNECT)
+        self.disconnect_button.set_is_important(True)
+        self.disconnect_button.set_tooltip_text("Disconnect from the server")
+        self.toolbar.insert(self.disconnect_button, 1)
+
+        self.toolbar.insert(gtk.SeparatorToolItem(), 2)
+
+        self.new_button = gtk.ToolButton(gtk.STOCK_NEW)
+        self.new_button.set_is_important(True)
+        self.toolbar.insert(self.new_button, 3)
+
+        self.edit_button = gtk.ToolButton(gtk.STOCK_EDIT)
+        self.edit_button.set_is_important(True)
+        self.toolbar.insert(self.edit_button, 4)
+
+        self.delete_button = gtk.ToolButton(gtk.STOCK_DELETE)
+        self.delete_button.set_is_important(True)
+        self.toolbar.insert(self.delete_button, 5)
+
+        # user list
+        self.users_groups_notebook = gtk.Notebook()
+        vbox.pack_start(self.users_groups_notebook, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        self.users_groups_notebook.append_page(scrolledwindow, gtk.Label("Users"))
+
+        self.users_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.users_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("")
+        renderer = gtk.CellRendererPixbuf()
+        renderer.set_property("pixbuf", gtk.gdk.pixbuf_new_from_file_at_size(self.user_icon_filename, 22, 22))
+        column.pack_start(renderer, True)
+        self.users_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.users_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Full Name")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.users_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Description")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.users_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("RID")
+        column.set_resizable(True)
+        column.set_sort_column_id(3)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.users_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 3)
+
+        self.users_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT)
+        self.users_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+        self.users_tree_view.set_model(self.users_store)
+
+        # group list
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        self.users_groups_notebook.append_page(scrolledwindow, gtk.Label("Groups"))
+
+        self.groups_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.groups_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Icon")
+        renderer = gtk.CellRendererPixbuf()
+        renderer.set_property("pixbuf", gtk.gdk.pixbuf_new_from_file_at_size(self.group_icon_filename, 22, 22))
+        column.pack_start(renderer, True)
+        self.groups_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.groups_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Description")
+        column.set_resizable(True)
+        column.set_sort_column_id(1)
+        column.set_expand(True)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.groups_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("RID")
+        column.set_resizable(True)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.groups_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        self.groups_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT)
+        self.groups_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+        self.groups_tree_view.set_model(self.groups_store)
+
+
+        # status bar
+
+        self.statusbar = gtk.Statusbar()
+        self.statusbar.set_has_resize_grip(True)
+        vbox.pack_start(self.statusbar, False, False, 0)
+
+
+        # signals/events
+
+        self.connect("delete_event", self.on_self_delete)
+        self.connect("key-press-event", self.on_key_press)
+
+        self.connect_item.connect("activate", self.on_connect_item_activate)
+        self.disconnect_item.connect("activate", self.on_disconnect_item_activate)
+        self.sel_domain_item.connect("activate", self.on_sel_domain_item_activate)
+        self.quit_item.connect("activate", self.on_quit_item_activate)
+        self.refresh_item.connect("activate", self.on_refresh_item_activate)
+        self.new_item.connect("activate", self.on_new_item_activate)
+        self.delete_item.connect("activate", self.on_delete_item_activate)
+        self.edit_item.connect("activate", self.on_edit_item_activate)
+        self.user_rights_item.connect("activate", self.on_user_rights_item_activate)
+        self.audit_item.connect("activate", self.on_audit_item_activate)
+        self.trust_relations_item.connect("activate", self.on_trust_relations_item_activate)
+        self.about_item.connect("activate", self.on_about_item_activate)
+
+        self.connect_button.connect("clicked", self.on_connect_item_activate)
+        self.disconnect_button.connect("clicked", self.on_disconnect_item_activate)
+        self.new_button.connect("clicked", self.on_new_item_activate)
+        self.delete_button.connect("clicked", self.on_delete_item_activate)
+        self.edit_button.connect("clicked", self.on_edit_item_activate)
+
+        self.users_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.users_tree_view.connect("button_press_event", self.on_users_tree_view_button_press)
+        self.groups_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.groups_tree_view.connect("button_press_event", self.on_groups_tree_view_button_press)
+        self.users_groups_notebook.connect("switch-page", self.on_users_groups_notebook_switch_page)
+
+        self.add_accel_group(accel_group)
+
+
+    def refresh_user_list_view(self):
+        if not self.connected():
+            return None
+
+        (model, paths) = self.users_tree_view.get_selection().get_selected_rows()
+
+        self.users_store.clear()
+        for user in self.pipe_manager.user_list:
+            self.users_store.append(user.list_view_representation())
+
+        if (len(paths) > 0):
+            self.users_tree_view.get_selection().select_path(paths[0])
+
+    def refresh_group_list_view(self):
+        if not self.connected():
+            return None
+
+        (model, paths) = self.groups_tree_view.get_selection().get_selected_rows()
+
+        self.groups_store.clear()
+        for group in self.pipe_manager.group_list:
+            self.groups_store.append(group.list_view_representation())
+
+        if len(paths) > 0:
+            self.groups_tree_view.get_selection().select_path(paths[0])
+
+    def get_selected_user(self):
+        if not self.connected():
+            return None
+
+        (model, iter) = self.users_tree_view.get_selection().get_selected()
+        if iter is None: # no selection
+            return None
+        else:
+            username = model.get_value(iter, 0)
+            user_list = [user for user in self.pipe_manager.user_list if user.username == username]
+            if len(user_list) > 0:
+                return user_list[0]
+            else:
+                return None
+
+    def get_selected_group(self):
+        if not self.connected():
+            return None
+
+        (model, iter) = self.groups_tree_view.get_selection().get_selected()
+        if iter is None: # no selection
+            return None
+        else:
+            name = model.get_value(iter, 0)
+            group_list = [group for group in self.pipe_manager.group_list if group.name == name]
+            if len(group_list) > 0:
+                return group_list[0]
+            else:
+                return None
+
+    def set_status(self, message):
+        self.statusbar.pop(0)
+        self.statusbar.push(0, message)
+
+    def update_sensitivity(self):
+        connected = (self.pipe_manager is not None)
+        user_selected = (self.get_selected_user() is not None)
+        group_selected = (self.get_selected_group() is not None)
+        selected = [user_selected, group_selected][self.users_groups_notebook_page_num]
+
+        self.connect_item.set_sensitive(not connected)
+        self.disconnect_item.set_sensitive(connected)
+        self.sel_domain_item.set_sensitive(connected)
+        self.refresh_item.set_sensitive(connected)
+        self.new_item.set_sensitive(connected)
+        self.delete_item.set_sensitive(connected and selected)
+        self.edit_item.set_sensitive(connected and selected)
+        self.user_rights_item.set_sensitive(connected)
+        self.audit_item.set_sensitive(connected)
+        self.trust_relations_item.set_sensitive(connected)
+
+        self.connect_button.set_sensitive(not connected)
+        self.disconnect_button.set_sensitive(connected)
+        self.new_button.set_sensitive(connected)
+        self.delete_button.set_sensitive(connected and selected)
+        self.edit_button.set_sensitive(connected and selected)
+
+    def update_captions(self):
+        self.user_group_item.get_child().set_text(["Users", "Groups"][self.users_groups_notebook_page_num > 0])
+        self.new_button.set_tooltip_text(["Create a new user", "Create a new group"][self.users_groups_notebook_page_num > 0])
+        self.edit_button.set_tooltip_text(["Edit user's properties", "Edit group's properties"][self.users_groups_notebook_page_num > 0])
+        self.delete_button.set_tooltip_text(["Delete the user", "Delete the group"][self.users_groups_notebook_page_num > 0])
+
+    def run_message_dialog(self, type, buttons, message, parent=None):
+        if parent is None:
+            parent = self
+
+        message_box = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, type,
+            buttons, message)
+        response = message_box.run()
+        message_box.hide()
+
+        return response
+
+    def run_user_edit_dialog(self, user=None, apply_callback=None):
+        dialog = UserEditDialog(self.pipe_manager, user)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]:
+                problem_msg = dialog.check_for_problems()
+
+                if problem_msg is not None:
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg, dialog)
+                else:
+                    dialog.values_to_user()
+
+                    if apply_callback is not None: #seems like there's only a callback when a user is modified, never when creating a new user.
+                        apply_callback(dialog.user)
+                        dialog.user_to_values()
+
+                    if response_id == gtk.RESPONSE_OK:
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        return dialog.user
+
+    def run_group_edit_dialog(self, group=None, apply_callback=None):
+        dialog = GroupEditDialog(self.pipe_manager, group)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]:
+                problem_msg = dialog.check_for_problems()
+
+                if problem_msg is not None:
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg, dialog)
+                else:
+                    dialog.values_to_group()
+
+                    if apply_callback is not None:
+                        apply_callback(dialog.thegroup)
+                        dialog.group_to_values()
+
+                    if response_id == gtk.RESPONSE_OK:
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        return dialog.thegroup
+
+    def run_connect_dialog(self, pipe_manager, server_address, transport_type,
+            username, password="", connect_now=False, domain_index=0,
+            domains=None):
+        connect_now2 = connect_now #this other value is used later on to skip domain selection.
+        #We need a second variable for this or else we would freeze if we had an error while connecting
+
+        dialog = SAMConnectDialog(server_address, transport_type, username, password)
+        dialog.show_all()
+
+        if (domains is None):
+            # loop to handle the failures
+            while True:
+                if connect_now:
+                    connect_now = False
+                    response_id = gtk.RESPONSE_OK
+                else:
+                    response_id = dialog.run()
+
+                if response_id != gtk.RESPONSE_OK:
+                    dialog.hide()
+                    return None
+                else:
+                    try:
+                        server_address = dialog.get_server_address()
+                        self.server_address = server_address
+                        transport_type = dialog.get_transport_type()
+                        self.transport_type = transport_type
+                        username = dialog.get_username()
+                        self.username = username
+                        domain_index = 0
+                        self.domain_index = domain_index
+                        password = dialog.get_password()
+
+                        pipe_manager = SAMPipeManager(server_address, transport_type, username, password)
+                        domains = pipe_manager.fetch_and_get_domain_names()
+                        break
+
+                    except RuntimeError, re:
+                        if re.args[1] == 'Logon failure': #user got the password wrong
+                            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Invalid username or password.", dialog)
+                            dialog.password_entry.grab_focus()
+                            dialog.password_entry.select_region(0, -1) #select all the text in the password box
+                        elif re.args[0] == 5 or re.args[1] == 'Access denied':
+                            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Access Denied.", dialog)
+                            dialog.username_entry.grab_focus()
+                            dialog.username_entry.select_region(0, -1)
+                        elif re.args[1] == 'NT_STATUS_HOST_UNREACHABLE':
+                            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Could not contact the server", dialog)
+                            dialog.server_address_entry.grab_focus()
+                            dialog.server_address_entry.select_region(0, -1)
+                        elif re.args[1] == 'NT_STATUS_NETWORK_UNREACHABLE':
+                            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The network is unreachable.\n\nPlease check your network connection.", dialog)
+                        else:
+                            msg = "Failed to connect: %s." % (re.args[1])
+                            print msg
+                            traceback.print_exc()
+                            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+                    except Exception, ex:
+                        msg = "Failed to connect: %s." % (str(ex))
+                        print msg
+                        traceback.print_exc()
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+        dialog.set_domains(domains, self.domain_index)
+        #return RESPONSE_OK if we were told to auto-connect. Otherwise run the dialog
+        response_id = connect_now2 and gtk.RESPONSE_OK or dialog.run()
+        dialog.hide()
+
+        if response_id != gtk.RESPONSE_OK:
+            return None
+        else:
+            self.domain_index = dialog.get_domain_index()
+            pipe_manager.set_current_domain(self.domain_index)
+
+        return pipe_manager
+
+    def connected(self):
+        return self.pipe_manager is not None
+
+    def update_user_callback(self, user):
+        try:
+            self.pipe_manager.update_user(user)
+
+            self.set_status("User \'%s\' updated." % (user.username))
+
+        except RuntimeError, re:
+            msg = "Failed to update user: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to update user: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        finally:
+            self.pipe_manager.fetch_user(user.rid, user) # to make sure we have the updated user properties. This has caught bugs already!
+            self.refresh_user_list_view()
+
+    def update_group_callback(self, group):
+        try:
+            self.pipe_manager.update_group(group)
+            self.set_status("Group \'%s\' updated." % (group.name))
+        except RuntimeError, re:
+            msg = "Failed to update group: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to update group: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        finally:
+            self.pipe_manager.fetch_group(group.rid, group) # just to make sure we have the updated group properties
+            self.refresh_group_list_view()
+
+    def on_key_press(self, widget, event):
+        if event.keyval == gtk.keysyms.F5:
+            self.on_refresh_item_activate(None)
+        elif event.keyval == gtk.keysyms.Delete:
+            self.on_delete_item_activate(None)
+        elif event.keyval == gtk.keysyms.Return:
+            myev = gtk.gdk.Event(gtk.gdk._2BUTTON_PRESS) #emulate a double-click
+            if self.users_groups_notebook_page_num == 0:
+                self.on_users_tree_view_button_press(None, myev)
+            else:
+                self.on_groups_tree_view_button_press(None, myev)
+
+    def on_self_delete(self, widget, event):
+        if (self.pipe_manager is not None):
+            self.on_disconnect_item_activate(self.disconnect_item)
+
+        gtk.main_quit()
+        return False
+
+    def on_connect_item_activate(self, widget, server="", transport_type=0,
+            username="", password="", connect_now=False, domain_index=0):
+        server = server or self.server_address
+        transport_type = transport_type or self.transport_type
+        username = username or self.username
+
+        try:
+            self.pipe_manager = self.run_connect_dialog(None, server,
+                transport_type, username, password, connect_now,
+                domain_index)
+            if self.pipe_manager is not None:
+                self.pipe_manager.fetch_users_and_groups()
+
+                self.set_status("Connected to %s/%s." % (
+                    self.server_address,
+                    SAMPipeManager.get_lsa_string(self.pipe_manager.domain[1])))
+
+        except RuntimeError, re:
+            msg = "Failed to open the selected domain: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to open the selected domain: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_user_list_view()
+        self.refresh_group_list_view()
+        self.update_sensitivity()
+
+    def on_disconnect_item_activate(self, widget):
+        if self.pipe_manager is not None:
+            self.pipe_manager.close()
+            self.pipe_manager = None
+
+        self.users_store.clear()
+        self.groups_store.clear()
+        self.update_sensitivity()
+
+        self.set_status("Disconnected.")
+
+    def on_sel_domain_item_activate(self, widget):
+        try:
+            self.pipe_manager = self.run_connect_dialog(self.pipe_manager,
+                    self.server_address, self.transport_type, self.username,
+                    domains = self.pipe_manager.fetch_and_get_domain_names())
+            if self.pipe_manager is not None:
+                self.pipe_manager.fetch_users_and_groups()
+
+                self.set_status("Connected to %s/%s." % (self.server_address,
+                    SAMPipeManager.get_lsa_string(self.pipe_manager.domain[1])))
+
+        except RuntimeError, re:
+            msg = "Failed to open the selected domain: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to open the selected domain: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_user_list_view()
+        self.refresh_group_list_view()
+        self.update_sensitivity()
+
+    def on_quit_item_activate(self, widget):
+        self.on_self_delete(None, None)
+
+    def on_refresh_item_activate(self, widget):
+        try:
+            self.pipe_manager.fetch_users_and_groups()
+        except RuntimeError, re:
+            msg = "Failed to refresh SAM info: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+        except Exception, ex:
+            msg = "Failed to refresh SAM info: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        self.refresh_user_list_view()
+        self.refresh_group_list_view()
+
+        self.set_status("Successfully refreshed Users and Groups.")
+
+        #deselect any selected groups and users
+        (model, iter) = self.users_tree_view.get_selection().get_selected()
+        if iter is None:
+            return
+        selector = self.users_tree_view.get_selection()
+        selector.unselect_iter(iter)
+
+        (model, iter) = self.groups_tree_view.get_selection().get_selected()
+        if iter is None:
+            return
+        selector = self.groups_tree_view.get_selection()
+        selector.unselect_iter(iter)
+
+    def on_new_item_activate(self, widget):
+        if self.users_groups_notebook_page_num == 0: # users tab
+            new_user = self.run_user_edit_dialog()
+            if new_user is None:
+                self.set_status("User creation canceled.")
+                return
+
+            try:
+                self.pipe_manager.add_user(new_user)
+                self.pipe_manager.fetch_users_and_groups()
+                self.set_status("Successfully created user \'%s\'." %
+                    (new_user.username))
+            except RuntimeError, re:
+                msg = "Failed to create user: %s." % (re.args[1])
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+            except Exception, ex:
+                msg = "Failed to create user: %s." % (str(ex))
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+            self.refresh_user_list_view()
+        else: # groups tab
+            new_group = self.run_group_edit_dialog()
+            if new_group is None:
+                return
+
+            try:
+                self.pipe_manager.add_group(new_group)
+                self.pipe_manager.fetch_users_and_groups()
+
+                self.set_status("Successfully created group \'%s\'." %
+                    (new_group.name,))
+            except RuntimeError, re:
+                msg = "Failed to create group: %s." % (re.args[1])
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+            except Exception, ex:
+                msg = "Failed to create group: %s." % (str(ex))
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+            self.refresh_group_list_view()
+
+    def on_delete_item_activate(self, widget):
+        if self.users_groups_notebook_page_num == 0: # users tab
+            del_user = self.get_selected_user()
+
+            if (self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to delete user '%s'?" % del_user.username) != gtk.RESPONSE_YES):
+                return
+
+            try:
+                self.pipe_manager.delete_user(del_user)
+                self.pipe_manager.fetch_users_and_groups()
+
+                self.set_status("Successfully deleted user \'%s\'." % (del_user.username))
+            except RuntimeError, re:
+                msg = "Failed to delete user: %s." % (re.args[1])
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+            except Exception, ex:
+                msg = "Failed to delete user: %s." % (str(ex))
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+            self.refresh_user_list_view()
+
+        else: # groups tab
+            del_group = self.get_selected_group()
+
+            if (self.run_message_dialog(gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to delete group '%s'?" % del_group.name) != gtk.RESPONSE_YES):
+                return
+
+            try:
+                self.pipe_manager.delete_group(del_group)
+                self.pipe_manager.fetch_users_and_groups()
+                self.set_status("Successfully deleted group \'%s\'." %
+                        (del_group.name))
+            except RuntimeError, re:
+                msg = "Failed to delete group: %s." % (re.args[1])
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+            except Exception, ex:
+                msg = "Failed to delete group: %s." % (str(ex))
+                self.set_status(msg)
+                print msg
+                traceback.print_exc()
+                self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+            self.refresh_group_list_view()
+
+    def on_edit_item_activate(self, widget):
+        if self.users_groups_notebook_page_num == 0: # users tab
+            edit_user = self.get_selected_user()
+            self.run_user_edit_dialog(edit_user, self.update_user_callback)
+        else: # groups tab
+            edit_group = self.get_selected_group()
+            self.run_group_edit_dialog(edit_group, self.update_group_callback)
+
+    def on_user_rights_item_activate(self, widget):
+        pass
+
+    def on_audit_item_activate(self, widget):
+        pass
+
+    def on_trust_relations_item_activate(self, widget):
+        pass
+
+    def on_about_item_activate(self, widget):
+        dialog = AboutDialog("PyGWSAM",
+            "A tool to manage accounts on a SAM server.\n"
+            "Based on Jelmer Vernooij's original Samba-GTK",
+            self.icon_pixbuf)
+        dialog.run()
+        dialog.hide()
+
+    def on_users_tree_view_button_press(self, widget, event):
+        if self.get_selected_user() is None:
+            return
+
+        if event.type == gtk.gdk._2BUTTON_PRESS:
+            self.on_edit_item_activate(self.edit_item)
+
+    def on_groups_tree_view_button_press(self, widget, event):
+        if self.get_selected_group() is None:
+            return
+
+        if event.type == gtk.gdk._2BUTTON_PRESS:
+            self.on_edit_item_activate(self.edit_item)
+
+    def on_users_groups_notebook_switch_page(self, widget, page, page_num):
+        self.users_groups_notebook_page_num = page_num # workaround - the signal is emitted before the actual change
+        self.update_captions()
+        self.update_sensitivity()
+
+    def on_update_sensitivity(self, widget):
+        self.update_sensitivity()
+
+
+def PrintUsage():
+    print "Usage: %s [OPTIONS]" % (str(os.path.split(__file__)[-1]))
+    print "All options are optional. The user will be queried for additional information if needed.\n"
+    print "  -s  --server\t\tspecify the server to connect to."
+    print "  -u  --user\t\tspecify the user."
+    print "  -p  --password\tThe password for the user."
+    print "  -t  --transport\tTransport type.\n\t\t\t\t0 for RPC, SMB, TCP/IP\n\t\t\t\t1 for RPC, TCP/IP\n\t\t\t\t2 for localhost."
+    print "  -c  --connect-now\tSkip the connect dialog."
+    #TODO: mention domain index. And maybe come up with a better way of handling it?
+
+def ParseArgs(argv):
+    arguments = {}
+
+    try: #get arguments into a nicer format
+        opts, args = getopt.getopt(argv, "chu:s:p:t:", ["help", "user=", "server=", "password=", "connect-now", "transport="])
+    except getopt.GetoptError:
+        PrintUsage()
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt in ("-h", "--help"):
+            PrintUsage()
+            sys.exit(0)
+        elif opt in ("-s", "--server"):
+            arguments.update({"server":arg})
+        elif opt in ("-u", "--user"):
+            arguments.update({"username":arg})
+        elif opt in ("-p", "--password"):
+            arguments.update({"password":arg})
+        elif opt in ("-t", "--transport"):
+            arguments.update({"transport_type":int(arg)})
+        elif opt in ("-c", "--connect-now"):
+            arguments.update({"connect_now":True})
+    return (arguments)
+
+
+
+if __name__ == "__main__":
+    arguments = ParseArgs(sys.argv[1:]) #the [1:] ignores the first argument, which is the path to our utility
+
+    main_window = SAMWindow(**arguments)
+    main_window.show_all()
+    gtk.main()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/pygwsvcctl.py b/build/lib.linux-x86_64-2.7/sambagtk/pygwsvcctl.py
new file mode 100644 (file)
index 0000000..7e3da03
--- /dev/null
@@ -0,0 +1,1040 @@
+#!/usr/bin/python
+
+import sys
+import os.path
+import traceback
+import threading
+import time
+import getopt
+
+import gobject
+import gtk
+import pango
+
+from samba import credentials
+from samba.dcerpc import svcctl
+
+from sambagtk.dialogs import (
+    AboutDialog,
+    )
+
+from sambagtk.svcctl import (
+    SvcCtlConnectDialog,
+    Service,
+    ServiceEditDialog,
+    ServiceControlDialog,
+    )
+
+
+class SvcCtlPipeManager(object):
+
+    def __init__(self, server_address, transport_type, username, password):
+        self.service_list = []
+        self.lock = threading.Lock()
+
+        creds = credentials.Credentials()
+        if (username.count("\\") > 0):
+            creds.set_domain(username.split("\\")[0])
+            creds.set_username(username.split("\\")[1])
+        elif (username.count("@") > 0):
+            creds.set_domain(username.split("@")[1])
+            creds.set_username(username.split("@")[0])
+        else:
+            creds.set_domain("")
+            creds.set_username(username)
+        creds.set_workstation("")
+        creds.set_password(password)
+
+        binding = ["ncacn_np:%s", "ncacn_ip_tcp:%s", "ncalrpc:%s"][transport_type]
+
+        self.pipe = svcctl.svcctl(binding % (server_address), credentials = creds)
+        self.scm_handle = self.pipe.OpenSCManagerA(None, None, svcctl.SC_MANAGER_ALL_ACCESS)
+
+    def close(self):
+        pass # apparently there's no .Close() method for this pipe
+
+    def fetch_services(self, svcctl_window):
+        """Fetches a list of services from the server. svcctl_window is used to update the GUI."""
+        # Note: this function is designed to be called by a secondary thread only.
+        # If the main thread calls this then THERE WILL BE A DEADLOCK!
+        del self.service_list[:]
+
+        (buffer, needed, count, resume_handle) = self.pipe.EnumServicesStatusW(self.scm_handle,
+            svcctl.SERVICE_TYPE_WIN32_OWN_PROCESS | svcctl.SERVICE_TYPE_WIN32_SHARE_PROCESS,
+            svcctl.SERVICE_STATE_ALL, 256 * 1024,
+            0)
+
+        runtime_error = None
+        current = 0.0
+        gtk.gdk.threads_enter()
+        svcctl_window.progressbar.show()
+        gtk.gdk.threads_leave()
+
+        for enum_service_status in SvcCtlPipeManager.enum_service_status_list_from_buffer(buffer, count):
+            try:
+                gtk.gdk.threads_enter()
+                svcctl_window.set_status("Fetching service: %s." % (enum_service_status.service_name))
+                svcctl_window.progressbar.set_fraction(current / count)
+                gtk.gdk.threads_leave()
+                current += 1.0
+                service = SvcCtlPipeManager.fetch_service(self, enum_service_status.service_name)
+                self.service_list.append(service)
+            except RuntimeError as re:
+                if re.args[0] == 5: #5 is WERR_ACCESS_DENIED
+                    print "Failed to fetch service \'%s\': Access Denied." % (enum_service_status.service_name)
+                else:
+                    #we do this so we can continue fetching services, even if a few fetches fail.
+                    print "Failed to fetch service %s: %s." % (enum_service_status.service_name, re.args[1])
+                    traceback.print_exc()
+
+        gtk.gdk.threads_enter()
+        svcctl_window.progressbar.hide()
+        gtk.gdk.threads_leave()
+
+    def start_service(self, service):
+        self.pipe.StartServiceW(service.handle, service.start_params.split())
+
+    def control_service(self, service, control):
+        self.pipe.ControlService(service.handle, control)
+
+    def update_service(self, service):
+        (service_config, needed) = self.pipe.QueryServiceConfigW(
+            service.handle, 8192)
+        # TODO: this gives a "DCERPC Fault NDR" error
+        #TODO: this gives me a "NT_STATUS_RPC_BAD_STUB_DATA" error
+        self.pipe.ChangeServiceConfigW(
+            service.handle,                      #policy_handle *handle
+            service_config.service_type,                #uint32 type
+            service.start_type,               #svcctl_StartType start_type
+            service_config.error_control,  #svcctl_ErrorControl error_control
+            unicode(service.path_to_exe),               #uint16 *binary_path
+            unicode(service_config.loadordergroup),     #uint16 *load_order_group
+            unicode(service_config.dependencies),       #uint16 *dependencies
+            unicode(service.account),                                #uint16 *service_start_name
+            unicode(service.account_password),          #uint16 *password
+            unicode(service.display_name))              #uint16 *display_name
+
+        service_status = self.pipe.QueryServiceStatus(service.handle)
+
+        if (service.allow_desktop_interaction):
+            service_status.type |= svcctl.SERVICE_TYPE_INTERACTIVE_PROCESS
+        else:
+            service_status.type &= ~svcctl.SERVICE_TYPE_INTERACTIVE_PROCESS
+
+        # TODO: apparently this call is not implemented
+        self.pipe.SetServiceStatus(service.handle, service_status)
+
+        self.pipe.CloseServiceHandle(service.handle)
+
+    def fetch_service(self, service_name):
+        service = Service()
+        service.name = service_name.strip()
+        service.handle = self.pipe.OpenServiceW(self.scm_handle, unicode(service_name), svcctl.SERVICE_ALL_ACCESS)
+
+        service_status = self.pipe.QueryServiceStatus(service.handle)
+        service.state = service_status.state
+        service.wait_hint = service_status.wait_hint
+        service.check_point = service_status.check_point
+        service.accepts_pause = (service_status.controls_accepted & svcctl.SVCCTL_ACCEPT_PAUSE_CONTINUE) != 0
+        service.accepts_stop = (service_status.controls_accepted & svcctl.SVCCTL_ACCEPT_STOP) != 0
+
+        (service_config, needed) = self.pipe.QueryServiceConfigW(service.handle, 8192)
+        service.display_name = service_config.displayname.strip()
+        service.start_type = service_config.start_type
+        service.path_to_exe = service_config.executablepath
+
+        if service_config.startname == "LocalSystem":
+            service.account = None
+        else:
+            service.account = service_config.startname
+
+        service.allow_desktop_interaction = (service_status.type & svcctl.SERVICE_TYPE_INTERACTIVE_PROCESS != 0)
+
+        (service_config2_buffer, needed) = self.pipe.QueryServiceConfig2W(service.handle, svcctl.SERVICE_CONFIG_DESCRIPTION, 8192)
+        service_description = SvcCtlPipeManager.service_description_from_buffer(service_config2_buffer)
+        service.description = service_description.description.strip()
+
+        return service
+
+    def fetch_service_status(self, service):
+        service_status = self.pipe.QueryServiceStatus(service.handle)
+
+        service.state = service_status.state
+        service.wait_hint = service_status.wait_hint
+        service.check_point = service_status.check_point
+        service.accepts_pause = (service_status.controls_accepted & svcctl.SVCCTL_ACCEPT_PAUSE_CONTINUE) != 0
+        service.accepts_stop = (service_status.controls_accepted & svcctl.SVCCTL_ACCEPT_STOP) != 0
+
+    @staticmethod
+    def enum_service_status_list_from_buffer(buffer, count):
+        enum_service_status_list = []
+        offset = 0
+
+        while count > 0:
+            enum_service_status = svcctl.ENUM_SERVICE_STATUSW()
+
+            addr = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            enum_service_status.service_name = SvcCtlPipeManager.get_nbo_ustring(buffer, addr)
+            offset += 4
+
+            addr = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            enum_service_status.display_name = SvcCtlPipeManager.get_nbo_ustring(buffer, addr)
+            offset += 4
+
+            enum_service_status.status = svcctl.SERVICE_STATUS()
+            enum_service_status.status.type = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.state = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.controls_accepted = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.win32_exit_code = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.service_exit_code = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.check_point = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status.status.wait_hint = SvcCtlPipeManager.get_nbo_long(buffer, offset)
+            offset += 4
+
+            enum_service_status_list.append(enum_service_status)
+
+            count -= 1
+
+        return enum_service_status_list
+
+    @staticmethod
+    def service_description_from_buffer(buffer):
+        service_description = svcctl.SERVICE_DESCRIPTION()
+
+        addr = SvcCtlPipeManager.get_nbo_long(buffer, 0)
+        service_description.description = SvcCtlPipeManager.get_nbo_ustring(buffer, addr)
+
+        return service_description
+
+    @staticmethod
+    def get_nbo_short(buffer, offset):
+        return ((buffer[offset + 1] << 8) + buffer[offset])
+
+    @staticmethod
+    def get_nbo_long(buffer, offset):
+        return ((((((buffer[offset + 3] << 8) + buffer[offset + 2]) << 8) + buffer[offset + 1]) << 8) + buffer[offset])
+
+    @staticmethod
+    def get_nbo_ustring(buffer, offset):
+        index = 0
+        string = u""
+
+        short = SvcCtlPipeManager.get_nbo_short(buffer, offset + index)
+        index += 2
+        while (short != 0):
+            string += unichr(short)
+            short = SvcCtlPipeManager.get_nbo_short(buffer, offset + index)
+            index += 2
+
+        return string
+
+
+class FetchServicesThread(threading.Thread):
+
+    def __init__(self, pipe_manager, svcctl_window):
+        super(FetchServicesThread, self).__init__()
+
+        self.pipe_manager = pipe_manager
+        self.svcctl_window = svcctl_window
+
+    def run(self):
+        self.pipe_manager.lock.acquire()
+        try:
+            #This function handles runtime errors on it's own. This way it can continue running after an error.
+            self.pipe_manager.fetch_services(self.svcctl_window)
+        finally:
+            self.pipe_manager.lock.release()
+
+        gtk.gdk.threads_enter()
+        self.svcctl_window.set_status("Connected to %s." % (self.svcctl_window.server_address))
+        self.svcctl_window.refresh_services_tree_view()
+        gtk.gdk.threads_leave()
+
+
+class ServiceControlThread(threading.Thread):
+
+    def __init__(self, pipe_manager, service, control, svcctl_window, service_control_dialog):
+        super(ServiceControlThread, self).__init__()
+
+        self.pipe_manager = pipe_manager
+        self.service = service
+        self.control = control
+        self.svcctl_window = svcctl_window
+        self.service_control_dialog = service_control_dialog
+        self.running = False
+        self.pending = False
+
+    def stop(self):
+        self.pending = True
+        self.running = False
+
+    def run(self):
+        self.running = True
+
+        control_string = {None: "start", svcctl.SVCCTL_CONTROL_STOP: "stop", svcctl.SVCCTL_CONTROL_PAUSE: "pause", svcctl.SVCCTL_CONTROL_CONTINUE: "resume"}
+        control_string2 = {None: "started", svcctl.SVCCTL_CONTROL_STOP: "stopped", svcctl.SVCCTL_CONTROL_PAUSE: "paused", svcctl.SVCCTL_CONTROL_CONTINUE: "resumed"}
+        final_state = {None: svcctl.SVCCTL_RUNNING, svcctl.SVCCTL_CONTROL_STOP: svcctl.SVCCTL_STOPPED, svcctl.SVCCTL_CONTROL_PAUSE: svcctl.SVCCTL_PAUSED, svcctl.SVCCTL_CONTROL_CONTINUE: svcctl.SVCCTL_RUNNING}
+        sleep_delay = 0.1
+
+        try:
+            self.pipe_manager.lock.acquire()
+
+            if (self.control is None): # starting
+                self.pipe_manager.start_service(self.service)
+            else:
+                self.pipe_manager.control_service(self.service, self.control)
+
+            self.pipe_manager.fetch_service_status(self.service)
+            if (self.service.wait_hint == 0):
+                self.service_control_dialog.set_progress_speed(0.5)
+            else:
+                self.service_control_dialog.set_progress_speed(1.0 / ((self.service.wait_hint / 1000.0) / sleep_delay))
+
+        except RuntimeError, re:
+            msg = "Failed to %s service \'%s\': %s." % (control_string[self.control], self.service.display_name, re.args[1])
+            print msg
+            traceback.print_exc()
+
+            gtk.gdk.threads_enter()
+            self.service_control_dialog.hide()
+            self.svcctl_window.set_status(msg)
+            self.svcctl_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, self.service_control_dialog)
+            gtk.gdk.threads_leave()
+
+            return
+
+        except Exception, ex:
+            msg = "Failed to %s service \'%s\': %s." % (control_string[self.control], self.service.display_name, str(ex))
+            print msg
+            traceback.print_exc()
+
+            gtk.gdk.threads_enter()
+            self.service_control_dialog.hide()
+            self.svcctl_window.set_status(msg)
+            self.svcctl_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, self.service_control_dialog)
+            gtk.gdk.threads_leave()
+
+            return
+
+        finally:
+            self.pipe_manager.lock.release()
+
+        while self.running:
+            try:
+                self.pipe_manager.lock.acquire()
+                self.pipe_manager.fetch_service_status(self.service)
+
+            except RuntimeError, re:
+                msg = "Failed to %s service \'%s\': %s." % (control_string[self.control], self.service.display_name, re.args[1])
+                print msg
+                traceback.print_exc()
+
+                gtk.gdk.threads_enter()
+                self.service_control_dialog.hide()
+                self.svcctl_window.set_status(msg)
+                self.svcctl_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, self.service_control_dialog)
+                gtk.gdk.threads_leave()
+
+                return
+
+            except Exception, ex:
+                msg = "Failed to %s service \'%s\': %s." % (control_string[self.control], self.service.display_name, str(ex))
+                print msg
+                traceback.print_exc()
+
+                gtk.gdk.threads_enter()
+                self.service_control_dialog.hide()
+                self.svcctl_window.set_status(msg)
+                self.svcctl_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, self.service_control_dialog)
+                gtk.gdk.threads_leave()
+
+                return
+
+            finally:
+                self.pipe_manager.lock.release()
+
+            if (self.service.state != svcctl.SVCCTL_START_PENDING and
+                self.service.state != svcctl.SVCCTL_STOP_PENDING and
+                self.service.state != svcctl.SVCCTL_PAUSE_PENDING and
+                self.service.state != svcctl.SVCCTL_CONTINUE_PENDING): # no pending operation => done
+
+                self.running = False
+                gtk.gdk.threads_enter()
+                self.service_control_dialog.progress(True)
+                gtk.gdk.threads_leave()
+
+            else:
+                gtk.gdk.threads_enter()
+                self.service_control_dialog.progress()
+                gtk.gdk.threads_leave()
+
+            time.sleep(sleep_delay)
+
+        gtk.gdk.threads_enter()
+        self.service_control_dialog.hide()
+        self.svcctl_window.refresh_services_tree_view()
+
+        if (self.service.state == final_state[self.control]):
+            self.svcctl_window.set_status("Successfully %s \'%s\' service." % (control_string2[self.control], self.service.display_name))
+        else:
+            if (self.pending):
+                self.svcctl_window.set_status("The %s operation on the \'%s\' service is still pending." % (control_string[self.control], self.service.display_name))
+            else:
+                msg = "Failed to %s the \'%s\' service." % (control_string[self.control], self.service.display_name)
+                self.svcctl_window.set_status(msg)
+                self.svcctl_window.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, self.svcctl_window)
+        gtk.gdk.threads_leave()
+
+
+class SvcCtlWindow(gtk.Window):
+
+    def __init__(self, info_callback = None, server = "", username = "", password = "", transport_type = 0, connect_now = False):
+        super(SvcCtlWindow, self).__init__()
+        # Note: Any change to these arguments should probably also be changed
+        # in on_connect_item_activate()
+
+        self.create()
+        self.pipe_manager = None
+        self.update_sensitivity()
+        self.update_captions()
+        self.set_status("Disconnected.")
+
+        # It's nice to have this info saved when a user wants to reconnect
+        self.server_address = server
+        self.username = username
+        self.transport_type = transport_type
+
+        self.on_connect_item_activate(None, server, transport_type, username, password, connect_now)
+
+        # This is used so the parent program can grab the server info after
+        # we've connected.
+        if info_callback is not None:
+            info_callback(server = self.server_address, username = self.username, transport_type = self.transport_type)
+
+    def create(self):
+
+        # main window
+
+        accel_group = gtk.AccelGroup()
+
+        self.set_title("Service Control Management")
+        self.set_default_size(800, 600)
+        self.icon_filename = os.path.join(sys.path[0], "images", "service.png")
+        self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(self.icon_filename)
+        self.set_icon(self.icon_pixbuf)
+
+        vbox = gtk.VBox(False, 0)
+        self.add(vbox)
+
+        # menu
+        self.menubar = gtk.MenuBar()
+        vbox.pack_start(self.menubar, False, False, 0)
+
+        self.file_item = gtk.MenuItem("_File")
+        self.menubar.add(self.file_item)
+
+        file_menu = gtk.Menu()
+        self.file_item.set_submenu(file_menu)
+
+        self.connect_item = gtk.ImageMenuItem(gtk.STOCK_CONNECT, accel_group)
+        file_menu.add(self.connect_item)
+
+        self.disconnect_item = gtk.ImageMenuItem(gtk.STOCK_DISCONNECT, accel_group)
+        file_menu.add(self.disconnect_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        file_menu.add(menu_separator_item)
+
+        self.quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, accel_group)
+        file_menu.add(self.quit_item)
+
+
+        self.view_item = gtk.MenuItem("_View")
+        self.menubar.add(self.view_item)
+
+        view_menu = gtk.Menu()
+        self.view_item.set_submenu(view_menu)
+
+        self.refresh_item = gtk.ImageMenuItem(gtk.STOCK_REFRESH, accel_group)
+        view_menu.add(self.refresh_item)
+
+        self.service_item = gtk.MenuItem("_Service")
+        self.menubar.add(self.service_item)
+
+        service_menu = gtk.Menu()
+        self.service_item.set_submenu(service_menu)
+
+        self.start_item = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PLAY, accel_group)
+        self.start_item.get_child().set_text("Start")
+        service_menu.add(self.start_item)
+
+        self.stop_item = gtk.ImageMenuItem(gtk.STOCK_MEDIA_STOP, accel_group)
+        self.stop_item.get_child().set_text("Stop")
+        service_menu.add(self.stop_item)
+
+        self.pause_resume_item = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PAUSE, accel_group)
+        service_menu.add(self.pause_resume_item)
+
+        menu_separator_item = gtk.SeparatorMenuItem()
+        service_menu.add(menu_separator_item)
+
+        self.properties_item = gtk.ImageMenuItem(gtk.STOCK_PROPERTIES, accel_group)
+        service_menu.add(self.properties_item)
+
+        self.help_item = gtk.MenuItem("_Help")
+        self.menubar.add(self.help_item)
+
+        help_menu = gtk.Menu()
+        self.help_item.set_submenu(help_menu)
+
+        self.about_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT, accel_group)
+        help_menu.add(self.about_item)
+
+        # toolbar
+        self.toolbar = gtk.Toolbar()
+        vbox.pack_start(self.toolbar, False, False, 0)
+
+        self.connect_button = gtk.ToolButton(gtk.STOCK_CONNECT)
+        self.connect_button.set_is_important(True)
+        self.connect_button.set_tooltip_text("Connect to a server")
+        self.toolbar.insert(self.connect_button, 0)
+
+        self.disconnect_button = gtk.ToolButton(gtk.STOCK_DISCONNECT)
+        self.disconnect_button.set_is_important(True)
+        self.disconnect_button.set_tooltip_text("Disconnect from the server")
+        self.toolbar.insert(self.disconnect_button, 1)
+
+        self.toolbar.insert(gtk.SeparatorToolItem(), 2)
+
+        self.start_button = gtk.ToolButton(gtk.STOCK_MEDIA_PLAY)
+        self.start_button.set_label("Start")
+        self.start_button.set_tooltip_text("Start the service")
+        self.start_button.set_is_important(True)
+        self.toolbar.insert(self.start_button, 3)
+
+        self.stop_button = gtk.ToolButton(gtk.STOCK_MEDIA_STOP)
+        self.stop_button.set_label("Stop")
+        self.stop_button.set_tooltip_text("Stop the service")
+        self.stop_button.set_is_important(True)
+        self.toolbar.insert(self.stop_button, 4)
+
+        self.pause_resume_button = gtk.ToolButton(gtk.STOCK_MEDIA_PAUSE)
+        self.pause_resume_button.set_is_important(True)
+        self.toolbar.insert(self.pause_resume_button, 5)
+
+        # services list
+        self.scrolledwindow = gtk.ScrolledWindow(None, None)
+        self.scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        vbox.pack_start(self.scrolledwindow, True, True, 0)
+
+        self.services_tree_view = gtk.TreeView()
+        self.scrolledwindow.add(self.services_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("")
+        column.set_resizable(False)
+        renderer = gtk.CellRendererPixbuf()
+        renderer.set_property("pixbuf", gtk.gdk.pixbuf_new_from_file_at_size(self.icon_filename, 22, 22))
+        column.pack_start(renderer, True)
+        self.services_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_fixed_width(80)
+        column.set_expand(True)
+        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.services_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Description")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.services_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("State")
+        column.set_resizable(True)
+        column.set_sort_column_id(3)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.services_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 3)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Start Type")
+        column.set_resizable(True)
+        column.set_sort_column_id(4)
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.services_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 4)
+
+        self.services_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        self.services_store.set_sort_column_id(1, gtk.SORT_ASCENDING)
+        self.services_tree_view.set_model(self.services_store)
+
+        # status bar & progress bar
+
+        self.statusbar = gtk.Statusbar()
+        self.statusbar.set_has_resize_grip(True)
+
+        self.progressbar = gtk.ProgressBar()
+        self.progressbar.set_no_show_all(True)
+        self.progressbar.hide()
+
+        hbox = gtk.HBox(False, 0)
+        hbox.pack_start(self.progressbar, False, False, 0)
+        hbox.pack_start(self.statusbar, True, True, 0)
+
+        vbox.pack_start(hbox, False, False, 0)
+
+        # signals/events
+
+        self.connect("delete_event", self.on_self_delete)
+        self.connect("key-press-event", self.on_key_press)
+
+        self.connect_item.connect("activate", self.on_connect_item_activate)
+        self.disconnect_item.connect("activate", self.on_disconnect_item_activate)
+        self.quit_item.connect("activate", self.on_quit_item_activate)
+        self.refresh_item.connect("activate", self.on_refresh_item_activate)
+        self.start_item.connect("activate", self.on_start_item_activate)
+        self.stop_item.connect("activate", self.on_stop_item_activate)
+        self.pause_resume_item.connect("activate", self.on_pause_resume_item_activate)
+        self.properties_item.connect("activate", self.on_properties_item_activate)
+        self.about_item.connect("activate", self.on_about_item_activate)
+
+        self.connect_button.connect("clicked", self.on_connect_item_activate)
+        self.disconnect_button.connect("clicked", self.on_disconnect_item_activate)
+        self.start_button.connect("clicked", self.on_start_item_activate)
+        self.stop_button.connect("clicked", self.on_stop_item_activate)
+        self.pause_resume_button.connect("clicked", self.on_pause_resume_item_activate)
+
+        self.services_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.services_tree_view.get_selection().connect("changed", self.on_update_captions)
+        self.services_tree_view.connect("button_press_event", self.on_services_tree_view_button_press)
+
+        self.add_accel_group(accel_group)
+
+
+    def refresh_services_tree_view(self):
+        if not self.connected():
+            return None
+
+        (model, paths) = self.services_tree_view.get_selection().get_selected_rows()
+
+        self.services_store.clear()
+
+        self.pipe_manager.lock.acquire()
+        for sevice in self.pipe_manager.service_list:
+            self.services_store.append(sevice.list_view_representation())
+        self.pipe_manager.lock.release()
+
+        if (len(paths) > 0):
+            self.services_tree_view.get_selection().select_path(paths[0])
+
+    def get_selected_service(self):
+        if not self.connected():
+            return None
+
+        (model, iter) = self.services_tree_view.get_selection().get_selected()
+        if (iter is None): # no selection
+            return None
+        else:
+            name = model.get_value(iter, 0)
+
+            self.pipe_manager.lock.acquire()
+            service_list = [service for service in self.pipe_manager.service_list if service.name == name]
+            self.pipe_manager.lock.release()
+
+            if (len(service_list) == 0):
+                return None
+            else:
+                return service_list[0]
+
+    def set_status(self, message):
+        self.statusbar.pop(0)
+        self.statusbar.push(0, message)
+
+    def update_sensitivity(self):
+        connected = (self.pipe_manager is not None)
+
+        service = self.get_selected_service()
+        if (service is not None):
+            selected = True
+            pausable = service.accepts_pause
+            stoppable = service.accepts_stop
+            startable = (service.start_type != svcctl.SVCCTL_DISABLED)
+
+            stopped = (service.state == svcctl.SVCCTL_STOPPED)
+            running = (service.state == svcctl.SVCCTL_RUNNING)
+            paused = (service.state == svcctl.SVCCTL_PAUSED)
+        else:
+            selected = False
+
+        self.connect_item.set_sensitive(not connected)
+        self.disconnect_item.set_sensitive(connected)
+        self.refresh_item.set_sensitive(connected)
+        self.start_item.set_sensitive(connected and selected and stopped and startable)
+        self.stop_item.set_sensitive(connected and selected and running and stoppable)
+        self.pause_resume_item.set_sensitive(connected and selected and pausable and (running or paused))
+        self.properties_item.set_sensitive(connected and selected)
+
+        self.connect_button.set_sensitive(not connected)
+        self.disconnect_button.set_sensitive(connected)
+        self.start_button.set_sensitive(connected and selected and stopped and startable)
+        self.stop_button.set_sensitive(connected and selected and running and stoppable)
+        self.pause_resume_button.set_sensitive(connected and selected and pausable and (running or paused))
+
+    def update_captions(self):
+        service = self.get_selected_service()
+        if (service is None):
+            paused = False
+        else:
+            paused = (service.state == svcctl.SVCCTL_PAUSED)
+
+        self.pause_resume_item.get_child().set_text(["Pause", "Resume"][paused])
+        self.pause_resume_button.set_tooltip_text(["Pause the service", "Resume the service"][paused])
+        self.pause_resume_button.set_label(["Pause", "Resume"][paused])
+
+    def run_message_dialog(self, type, buttons, message, parent=None):
+        if parent is None:
+            parent = self
+
+        message_box = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, type,
+                buttons, message)
+        response = message_box.run()
+        message_box.hide()
+
+        return response
+
+    def run_service_edit_dialog(self, service=None, apply_callback=None):
+        dialog = ServiceEditDialog(service)
+        dialog.show_all()
+
+        # loop to handle the applies
+        while True:
+            response_id = dialog.run()
+
+            if (response_id in [gtk.RESPONSE_OK, gtk.RESPONSE_APPLY]):
+                problem_msg = dialog.check_for_problems()
+
+                if (problem_msg is not None):
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, problem_msg, dialog)
+                else:
+                    dialog.values_to_service()
+                    if (apply_callback is not None):
+                        apply_callback(dialog.service)
+                    if (response_id == gtk.RESPONSE_OK):
+                        dialog.hide()
+                        break
+
+            else:
+                dialog.hide()
+                return None
+
+        return dialog.service
+
+    def run_service_control_dialog(self, service, control):
+        dialog = ServiceControlDialog(service, control)
+        thread = ServiceControlThread(self.pipe_manager, service, control, self, dialog)
+        dialog.set_close_callback(thread.stop)
+
+        dialog.show_all()
+        thread.start()
+
+        return dialog.service
+
+    def run_connect_dialog(self, pipe_manager, server_address, transport_type,
+            username, password="", connect_now=False):
+        dialog = SvcCtlConnectDialog(server_address, transport_type, username,
+            password)
+        dialog.show_all()
+
+        # loop to handle the failures
+        while True:
+            if (connect_now):
+                connect_now = False
+                response_id = gtk.RESPONSE_OK
+            else:
+                response_id = dialog.run()
+
+            if (response_id != gtk.RESPONSE_OK):
+                dialog.hide()
+                return None
+            else:
+                try:
+                    server_address = dialog.get_server_address()
+                    self.server_address = server_address
+                    transport_type = dialog.get_transport_type()
+                    self.transport_type = transport_type
+                    username = dialog.get_username()
+                    self.username = username
+                    password = dialog.get_password()
+
+                    pipe_manager = SvcCtlPipeManager(server_address, transport_type, username, password)
+
+                    break
+
+                except RuntimeError, re:
+                    if re.args[1] == 'Logon failure': #user got the password wrong
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Invalid username or password.", dialog)
+                        dialog.password_entry.grab_focus()
+                        dialog.password_entry.select_region(0, -1) #select all the text in the password box
+                    elif re.args[0] == 5 or re.args[1] == 'Access denied':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Access Denied.", dialog)
+                        dialog.username_entry.grab_focus()
+                        dialog.username_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_HOST_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: Could not contact the server.", dialog)
+                        dialog.server_address_entry.grab_focus()
+                        dialog.server_address_entry.select_region(0, -1)
+                    elif re.args[1] == 'NT_STATUS_NETWORK_UNREACHABLE':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The network is unreachable.\n\nPlease check your network connection.", dialog)
+                    elif re.args[1] == 'NT_STATUS_CONNECTION_REFUSED':
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to connect: The connection was refused.", dialog)
+                    else:
+                        msg = "Failed to connect: %s." % (re.args[1])
+                        print msg
+                        traceback.print_exc()
+                        self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+                except Exception, ex:
+                    msg = "Failed to connect: %s." % (str(ex))
+                    print msg
+                    traceback.print_exc()
+                    self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg, dialog)
+
+        dialog.hide()
+        return pipe_manager
+
+    def connected(self):
+        return self.pipe_manager is not None
+
+    def update_service_callback(self, service):
+        try:
+            self.pipe_manager.lock.acquire()
+            self.pipe_manager.update_service(service)
+            self.pipe_manager.fetch_service_status(service)
+
+            self.set_status("Service \'%s\' updated.") % (service.display_name)
+
+        except RuntimeError, re:
+            msg = "Failed to update service: %s." % (re.args[1])
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to update service: %s." % (str(ex))
+            print msg
+            self.set_status(msg)
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        finally:
+            self.pipe_manager.lock.release()
+
+        self.refresh_services_tree_view()
+
+    def on_key_press(self, widget, event):
+        if event.keyval == gtk.keysyms.F5: #refresh when F5 is pressed
+            self.on_refresh_item_activate(None)
+        elif event.keyval == gtk.keysyms.Return:
+            myev = gtk.gdk.Event(gtk.gdk._2BUTTON_PRESS) #emulate a double-click
+            self.on_services_tree_view_button_press(None, myev)
+
+    def on_self_delete(self, widget, event):
+        if (self.pipe_manager is not None):
+            self.on_disconnect_item_activate(self.disconnect_item)
+
+        gtk.main_quit()
+        return False
+
+    def on_connect_item_activate(self, widget, server="", transport_type=0,
+            username="", password="", connect_now=False):
+        server = server or self.server_address
+        transport_type = transport_type or self.transport_type
+        username = username or self.username
+
+        self.pipe_manager = self.run_connect_dialog(None, server,
+                transport_type, username, password, connect_now)
+        if (self.pipe_manager is not None):
+            self.set_status("Fetching services from %s..." % (server))
+
+            FetchServicesThread(self.pipe_manager, self).start()
+            #After this thread runs it will post a connected message.
+            #It's not ideal, but it's probably the best solution.
+
+        self.update_sensitivity()
+        self.update_captions()
+
+    def on_disconnect_item_activate(self, widget):
+        if (self.pipe_manager is not None):
+            self.pipe_manager.close()
+            self.pipe_manager = None
+
+        self.services_store.clear()
+        self.update_sensitivity()
+        self.update_captions()
+
+        self.set_status("Disconnected.")
+
+    def on_quit_item_activate(self, widget):
+        self.on_self_delete(None, None)
+
+    def on_refresh_item_activate(self, widget):
+        self.set_status("Fetching services from %s..." % (self.server_address))
+        FetchServicesThread(self.pipe_manager, self).start()
+
+    def on_start_item_activate(self, widget):
+        start_service = self.get_selected_service()
+        if (start_service is None):
+            return
+
+        self.run_service_control_dialog(start_service, None)
+
+    def on_stop_item_activate(self, widget):
+        stop_service = self.get_selected_service()
+        if (stop_service is None):
+            return
+
+        self.run_service_control_dialog(stop_service, svcctl.SVCCTL_CONTROL_STOP)
+
+    def on_pause_resume_item_activate(self, widget):
+        try:
+            pause_resume_service = self.get_selected_service()
+            if (pause_resume_service is None):
+                return
+
+            self.pipe_manager.lock.acquire()
+            self.pipe_manager.fetch_service_status(pause_resume_service)
+
+        except RuntimeError, re:
+            msg = "Failed to fetch service status: %s." % (re.args[1])
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        except Exception, ex:
+            msg = "Failed to fetch service status: %s." % (str(ex))
+            self.set_status(msg)
+            print msg
+            traceback.print_exc()
+            self.run_message_dialog(gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+
+        finally:
+            self.pipe_manager.lock.release()
+
+        if pause_resume_service.state == svcctl.SVCCTL_PAUSED:
+            self.run_service_control_dialog(pause_resume_service,
+                    svcctl.SVCCTL_CONTROL_CONTINUE)
+        elif pause_resume_service.state == svcctl.SVCCTL_RUNNING:
+            self.run_service_control_dialog(pause_resume_service,
+                    svcctl.SVCCTL_CONTROL_PAUSE)
+
+    def on_properties_item_activate(self, widget):
+        edit_service = self.get_selected_service()
+        self.run_service_edit_dialog(edit_service, self.update_service_callback)
+
+        self.set_status("Service \'%s\' updated." % (edit_service.display_name))
+
+    def on_about_item_activate(self, widget):
+        dialog = AboutDialog(
+            "PyGWSvcCtl",
+            "A tool to remotely manage the services on a computer.\n Based on Jelmer Vernooij's original Samba-GTK",
+            self.icon_pixbuf)
+        dialog.run()
+        dialog.hide()
+
+    def on_services_tree_view_button_press(self, widget, event):
+        if (self.get_selected_service() is None):
+            return
+
+        if (event.type == gtk.gdk._2BUTTON_PRESS):
+            self.on_properties_item_activate(self.properties_item)
+
+    def on_update_captions(self, widget):
+        self.update_captions()
+
+    def on_update_sensitivity(self, widget):
+        self.update_sensitivity()
+
+#************ END OF CLASS ***************
+
+def PrintUsage():
+    print "Usage: %s [OPTIONS]" % (str(os.path.split(__file__)[-1]))
+    print "All options are optional. The user will be queried for additional information if needed.\n"
+    print "  -s  --server\t\tspecify the server to connect to."
+    print "  -u  --user\t\tspecify the user."
+    print "  -p  --password\tThe password for the user."
+    print "  -t  --transport\tTransport type.\n\t\t\t\t0 for RPC, SMB, TCP/IP\n\t\t\t\t1 for RPC, TCP/IP\n\t\t\t\t2 for localhost."
+    print "  -c  --connect-now\tSkip the connect dialog."
+
+def ParseArgs(argv):
+    arguments = {}
+
+    try: #get arguments into a nicer format
+        opts, args = getopt.getopt(argv, "chu:s:p:t:", ["help", "user=", "server=", "password=", "connect-now", "transport="])
+    except getopt.GetoptError:
+        PrintUsage()
+        sys.exit(2)
+
+    for opt, arg in opts:
+        if opt in ("-h", "--help"):
+            PrintUsage()
+            sys.exit(0)
+        elif opt in ("-s", "--server"):
+            arguments.update({"server":arg})
+        elif opt in ("-u", "--user"):
+            arguments.update({"username":arg})
+        elif opt in ("-p", "--password"):
+            arguments.update({"password":arg})
+        elif opt in ("-t", "--transport"):
+            arguments.update({"transport_type":int(arg)})
+        elif opt in ("-c", "--connect-now"):
+            arguments.update({"connect_now":True})
+    return (arguments)
+
+"""
+    Info about the thread locks used in this utility:
+the pipe lock is <pipe manager instance>.lock.acquire() and .release()
+the gdk lock (main thread lock) is simply gtk.gdk.threads_enter() and .threads_leave(), no need to get an instance
+the gdk lock is automatically acquired and released with each iteration of the gtk.main() loop.
+    So that means every time a callback function is called in the main thread (for example on_connect_item_activate()),
+    it will automatically grab the lock, run the function, and release it afterwards
+If you have to, you may acquire both locks at the same time as long as you get the gdk lock first!
+"""
+if __name__ == "__main__":
+    arguments = ParseArgs(sys.argv[1:]) #the [1:] ignores the first argument, which is the path to our utility
+
+    gtk.gdk.threads_init()
+    main_window = SvcCtlWindow(**arguments)
+    main_window.show_all()
+    gtk.main()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/registry.py b/build/lib.linux-x86_64-2.7/sambagtk/registry.py
new file mode 100644 (file)
index 0000000..32e6924
--- /dev/null
@@ -0,0 +1,1906 @@
+# Samba GTK+ frontends
+#
+# Copyright (C) 2010 Sergio Martins <sergio97@gmail.com>
+# Copyright (C) 2011 Jelmer Vernooij <jelmer@samba.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+"""Registry-related dialogs."""
+
+import gtk
+import gobject
+import pango
+
+import os
+import string
+import sys
+
+from samba.dcerpc import misc
+
+class RegistryValue(object):
+
+    def __init__(self, name, type, data, parent):
+        self.name = name
+        self.type = type
+        self.data = data
+        self.parent = parent
+
+    def get_absolute_path(self):
+        if self.parent is None:
+            return self.name
+        else:
+            return self.parent.get_absolute_path() + "\\" + self.name
+
+    def get_data_string(self):
+        interpreted_data = self.get_interpreted_data()
+
+        if interpreted_data is None or len(self.data) == 0:
+            return "(value not set)"
+        elif self.type in (misc.REG_SZ, misc.REG_EXPAND_SZ):
+            return interpreted_data
+        elif self.type == misc.REG_BINARY:
+            return "".join(["%02X" % byte for byte in interpreted_data])
+        elif self.type == misc.REG_DWORD:
+            return "0x%08X" % (interpreted_data)
+        elif self.type == misc.REG_DWORD_BIG_ENDIAN:
+            return "0x%08X" % (interpreted_data)
+        elif self.type == misc.REG_MULTI_SZ:
+            return " ".join(interpreted_data)
+        elif self.type == misc.REG_QWORD:
+            return "0x%016X" % (interpreted_data)
+        else:
+            return str(interpreted_data)
+
+    def get_interpreted_data(self):
+        if self.data is None:
+            return None
+
+        if self.type in (misc.REG_SZ, misc.REG_EXPAND_SZ):
+            result = ""
+
+            index = 0
+            while (index + 1 < len(self.data)): #The +1 ensures that the whole char is valid. Corrupt keys can otherwise cause exceptions
+                word = ((self.data[index + 1] << 8) + self.data[index])
+                if word != 0:
+                    result += unichr(word)
+                index += 2
+
+            return result
+        elif self.type == misc.REG_BINARY:
+            return self.data
+        elif self.type == misc.REG_DWORD:
+            result = 0L
+
+            if len(self.data) < 4:
+                return 0L
+
+            for i in xrange(4):
+                result = (result << 8) + self.data[3 - i]
+
+            return result
+        elif self.type == misc.REG_DWORD_BIG_ENDIAN:
+            result = 0L
+
+            if len(self.data) < 4:
+                return 0L
+
+            for i in xrange(4):
+                result = (result << 8) + self.data[i]
+
+            return result
+        elif self.type == misc.REG_MULTI_SZ:
+            result = []
+            string = ""
+
+            if len(self.data) == 0:
+                return []
+
+            index = 0
+            while (index < len(self.data)):
+                word = ((self.data[index + 1] << 8) + self.data[index])
+                if word == 0:
+                    result.append(string)
+                    string = ""
+                else:
+                    string += unichr(word)
+
+                index += 2
+
+            result.pop() # remove last systematic empty string
+
+            return result
+        elif self.type == misc.REG_QWORD:
+            result = 0L
+
+            if len(self.data) < 8:
+                return 0L
+
+            for i in xrange(8):
+                result = (result << 8) + self.data[7 - i]
+
+            return result
+        else:
+            return self.data
+
+    def set_interpreted_data(self, data):
+        del self.data[:]
+
+        if data is None:
+            self.data = None
+        elif self.type in (misc.REG_SZ, misc.REG_EXPAND_SZ):
+            for uch in data:
+                word = ord(uch)
+                self.data.append(int(word & 0x00FF))
+                self.data.append(int((word >> 8) & 0x00FF))
+        elif self.type == misc.REG_BINARY:
+            self.data = []
+            for elem in data:
+                self.data.append(int(elem))
+        elif self.type == misc.REG_DWORD:
+            for i in xrange(4):
+                self.data.append(int(data >> (8 * i) & 0xFF))
+        elif self.type == misc.REG_DWORD_BIG_ENDIAN:
+            for i in xrange(3, -1, -1):
+                self.data.append(int(data >> (8 * i) & 0xFF))
+        elif self.type == misc.REG_MULTI_SZ:
+            index = 0
+
+            for string in data:
+                for uch in string:
+                    word = ord(uch)
+                    self.data.append(int(word & 0x00FF))
+                    self.data.append(int((word >> 8) & 0x00FF))
+
+                self.data.append(0)
+                self.data.append(0)
+
+            self.data.append(0)
+            self.data.append(0)
+        elif self.type == misc.REG_QWORD:
+            for i in xrange(8):
+                self.data.append(int(data >> (8 * i) & 0xFF))
+        else:
+            self.data = data
+
+    def list_view_representation(self):
+        return [self.name, RegistryValue.get_type_string(self.type),
+                self.get_data_string(), self]
+
+    @staticmethod
+    def get_type_string(type):
+        type_strings = {
+            misc.REG_SZ:"String",
+            misc.REG_BINARY:"Binary Data",
+            misc.REG_EXPAND_SZ:"Expandable String",
+            misc.REG_DWORD:"32-bit Number (little endian)",
+            misc.REG_DWORD_BIG_ENDIAN:"32-bit Number (big endian)",
+            misc.REG_MULTI_SZ:"Multi-String",
+            misc.REG_QWORD:"64-bit Number (little endian)"
+            }
+
+        return type_strings[type]
+
+
+class RegistryKey(object):
+
+    def __init__(self, name, parent):
+        self.name = name
+        self.parent = parent
+        self.handle = None
+
+    def get_absolute_path(self):
+        if self.parent is None:
+            return self.name
+        else:
+            return self.parent.get_absolute_path() + "\\" + self.name
+
+    def get_root_key(self):
+        if self.parent is None:
+            return self
+        else:
+            return self.parent.get_root_key()
+
+    def list_view_representation(self):
+        return [self.name, self]
+
+
+class RegValueEditDialog(gtk.Dialog):
+
+    def __init__(self, reg_value, type):
+        super(RegValueEditDialog, self).__init__()
+
+        if reg_value is None:
+            self.brand_new = True
+            self.reg_value = RegistryValue("", type, [], None)
+        else:
+            self.brand_new = False
+            self.reg_value = reg_value
+
+        self.disable_signals = False
+        self.ascii_cursor_shift = 0 # because moving the cursor in some functions has no effect, we need to keep this value and apply it at the right time
+        self.hex_cursor_shift = 0
+
+        self.create()
+        self.reg_value_to_values()
+
+    def create(self):
+        self.set_title(["Edit registry value", "New registry value"][self.brand_new])
+        self.set_border_width(5)
+
+        self.icon_registry_number_filename = os.path.join(sys.path[0],
+                "images", "registry-number.png")
+        self.icon_registry_string_filename = os.path.join(sys.path[0],
+                "images", "registry-string.png")
+        self.icon_registry_binary_filename = os.path.join(sys.path[0],
+                "images", "registry-binary.png")
+
+        self.set_resizable(True)
+
+        # value name
+        hbox = gtk.HBox()
+        self.vbox.pack_start(hbox, False, False, 10)
+
+        label = gtk.Label("Value name:")
+        hbox.pack_start(label, False, True, 10)
+
+        self.name_entry = gtk.Entry()
+        self.name_entry.set_activates_default(True)
+        self.name_entry.set_sensitive(self.brand_new)
+        hbox.pack_start(self.name_entry, True, True, 10)
+
+        separator = gtk.HSeparator()
+        self.vbox.pack_start(separator, False, True, 5)
+
+        # data
+        frame = gtk.Frame(" Value data: ")
+        self.vbox.pack_start(frame, True, True, 10)
+
+        self.type_notebook = gtk.Notebook()
+        self.type_notebook.set_border_width(5)
+        self.type_notebook.set_show_tabs(False)
+        self.type_notebook.set_show_border(False)
+        frame.add(self.type_notebook)
+
+        # string type page
+        self.string_data_entry = gtk.Entry()
+        self.string_data_entry.set_activates_default(True)
+        self.type_notebook.append_page(self.string_data_entry)
+
+
+        # binary type page
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
+        self.type_notebook.append_page(scrolledwindow)
+
+        hbox = gtk.HBox()
+        scrolledwindow.add_with_viewport(hbox)
+
+        self.binary_data_addr_text_view = gtk.TextView()
+        self.binary_data_addr_text_view.set_wrap_mode(gtk.WRAP_WORD)
+        self.binary_data_addr_text_view.set_editable(False)
+        self.binary_data_addr_text_view.modify_font(pango.FontDescription("mono 10"))
+        self.binary_data_addr_text_view.set_size_request(60, -1)
+        hbox.pack_start(self.binary_data_addr_text_view, False, False, 0)
+
+        self.binary_data_hex_text_view = gtk.TextView()
+        self.binary_data_hex_text_view.set_wrap_mode(gtk.WRAP_WORD)
+        self.binary_data_hex_text_view.set_accepts_tab(False)
+        self.binary_data_hex_text_view.modify_font(pango.FontDescription("mono bold 10"))
+        self.binary_data_hex_text_view.set_size_request(275, -1)
+        hbox.pack_start(self.binary_data_hex_text_view, False, False, 0)
+
+        self.binary_data_ascii_text_view = gtk.TextView()
+        self.binary_data_ascii_text_view.set_wrap_mode(gtk.WRAP_CHAR)
+        #self.binary_data_ascii_text_view.set_editable(False)
+        self.binary_data_ascii_text_view.modify_font(pango.FontDescription("mono 10"))
+        self.binary_data_ascii_text_view.set_accepts_tab(False)
+        self.binary_data_ascii_text_view.set_size_request(100, -1)
+        hbox.pack_start(self.binary_data_ascii_text_view, False, False, 0)
+
+
+        # number type page
+        hbox = gtk.HBox()
+        self.type_notebook.append_page(hbox)
+
+        self.number_data_entry = gtk.Entry()
+        self.number_data_entry.set_activates_default(True)
+        hbox.pack_start(self.number_data_entry, True, True, 5)
+
+        self.number_data_dec_radio = gtk.RadioButton(None, "Decimal")
+        hbox.pack_start(self.number_data_dec_radio, False, True, 5)
+
+        self.number_data_hex_radio = gtk.RadioButton(self.number_data_dec_radio, "Hexadecimal")
+        hbox.pack_start(self.number_data_hex_radio, False, True, 5)
+
+        self.number_data_hex_radio.set_active(True)
+
+        # multi-string type page
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        self.type_notebook.append_page(scrolledwindow)
+
+        self.multi_string_data_text_view = gtk.TextView()
+        self.multi_string_data_text_view.set_wrap_mode(gtk.WRAP_NONE)
+        scrolledwindow.add(self.multi_string_data_text_view)
+
+        # dialog buttons
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new task
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+        # signals/events
+        self.binary_data_hex_text_view.get_buffer().connect("changed", self.on_binary_data_hex_text_view_buffer_changed)
+        self.binary_data_hex_text_view.get_buffer().connect("insert-text", self.on_binary_data_hex_text_view_buffer_insert_text)
+        self.binary_data_hex_text_view.get_buffer().connect("delete-range", self.on_binary_data_hex_text_view_buffer_delete_range)
+
+        # Ascii text view callbacks. This view requires special attention to
+        # facilitate the crazy editing it needs to do
+        self.binary_data_ascii_text_view.get_buffer().connect("insert-text", self.on_binary_data_ascii_text_view_buffer_insert_text) #manually handles inserting text
+        self.binary_data_ascii_text_view.get_buffer().connect("delete-range", self.on_binary_data_ascii_text_view_buffer_delete_range) #manually handles deleting text
+        self.binary_data_ascii_text_view.get_buffer().connect("changed", self.on_binary_data_ascii_text_view_buffer_changed)
+        self.binary_data_ascii_text_view.connect("move-cursor", self.on_binary_data_ascii_text_view_move_cursor)
+
+        self.number_data_dec_radio.connect("toggled", self.on_number_data_dec_radio_toggled)
+        self.number_data_hex_radio.connect("toggled", self.on_number_data_hex_radio_toggled)
+        self.number_data_entry.connect("changed", self.on_number_data_entry_changed)
+
+    def check_for_problems(self):
+        if len(self.name_entry.get_text().strip()) == 0:
+            return "Please specify a name."
+
+        if self.reg_value.type in [misc.REG_DWORD, misc.REG_DWORD_BIG_ENDIAN, misc.REG_QWORD]:
+            number_str = self.number_data_entry.get_text()
+            if len(number_str) == 0:
+                return "Please enter a number."
+
+            if self.number_data_dec_radio.get_active():
+                try:
+                    number = string.atoi(number_str, 10)
+                except Exception:
+                    return "Please enter a valid decimal number."
+                else:
+                    number_str_hex = "%X" % number
+
+                    if self.reg_value.type in [misc.REG_DWORD, misc.REG_DWORD_BIG_ENDIAN]:
+                        max_hex_len = 8
+                    else:
+                        max_hex_len = 16
+
+                    if len(number_str_hex) > max_hex_len:
+                        return "Please enter a smaller decimal number."
+        return None
+
+    def reg_value_to_values(self):
+        if self.reg_value is None:
+            raise Exception("registry value not set")
+
+        self.name_entry.set_text(self.reg_value.name)
+
+        if self.reg_value.type in [misc.REG_SZ, misc.REG_EXPAND_SZ]:
+            self.set_icon_from_file(self.icon_registry_string_filename)
+            self.set_size_request(430, 200)
+
+            self.string_data_entry.set_text(self.reg_value.get_interpreted_data())
+        elif self.reg_value.type == misc.REG_BINARY:
+            self.set_icon_from_file(self.icon_registry_binary_filename)
+            self.set_size_request(483, 400) #extra few pixels for the scroll bar
+
+            self.binary_data_hex_text_view.get_buffer().set_text(RegValueEditDialog.byte_array_to_hex(self.reg_value.get_interpreted_data(), 8))
+            #self.on_binary_data_hex_text_view_buffer_changed(None) #this is already called with the statement above
+
+        elif self.reg_value.type in [misc.REG_DWORD, misc.REG_DWORD_BIG_ENDIAN, misc.REG_QWORD]:
+            self.set_icon_from_file(self.icon_registry_number_filename)
+            self.set_size_request(430, 200)
+
+            if self.reg_value.type == misc.REG_QWORD:
+                self.number_data_entry.set_text("%016X" % self.reg_value.get_interpreted_data())
+            else:
+                self.number_data_entry.set_text("%08X" % self.reg_value.get_interpreted_data())
+
+        elif self.reg_value.type == misc.REG_MULTI_SZ:
+            self.set_icon_from_file(self.icon_registry_string_filename)
+            self.set_size_request(430, 400)
+
+            text = ""
+            for line in self.reg_value.get_interpreted_data():
+                text += line + "\n"
+
+            self.multi_string_data_text_view.get_buffer().set_text(text)
+
+    def values_to_reg_value(self):
+        if self.reg_value is None:
+            raise Exception("registry value not set")
+
+        self.reg_value.name = self.name_entry.get_text()
+
+        if self.reg_value.type in [misc.REG_SZ, misc.REG_EXPAND_SZ]:
+            self.reg_value.set_interpreted_data(self.string_data_entry.get_text())
+        elif self.reg_value.type == misc.REG_BINARY:
+            buffer = self.binary_data_hex_text_view.get_buffer()
+            text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+            self.reg_value.set_interpreted_data(RegValueEditDialog.hex_to_byte_array(text))
+        elif self.reg_value.type in [misc.REG_DWORD, misc.REG_DWORD_BIG_ENDIAN, misc.REG_QWORD]:
+            if self.number_data_dec_radio.get_active():
+                self.reg_value.set_interpreted_data(string.atoi(self.number_data_entry.get_text(), 10))
+            else:
+                self.reg_value.set_interpreted_data(string.atoi(self.number_data_entry.get_text(), 0x10))
+
+        elif self.reg_value.type == misc.REG_MULTI_SZ:
+            lines = []
+            line = ""
+
+            buffer = self.multi_string_data_text_view.get_buffer()
+            for ch in buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()):
+                if ch != "\n":
+                    line += ch
+                else:
+                    lines.append(line)
+                    line = ""
+
+            if len(line) > 0:
+                lines.append(line)
+
+            self.reg_value.set_interpreted_data(lines)
+
+    def update_type_page_after_show(self):
+        if self.reg_value.type == misc.REG_SZ:
+            self.type_notebook.set_current_page(0)
+            self.string_data_entry.grab_focus()
+        if self.reg_value.type == misc.REG_EXPAND_SZ:
+            self.type_notebook.set_current_page(0)
+            self.string_data_entry.grab_focus()
+        if self.reg_value.type == misc.REG_BINARY:
+            self.type_notebook.set_current_page(1)
+            self.binary_data_hex_text_view.grab_focus()
+        if self.reg_value.type == misc.REG_DWORD:
+            self.type_notebook.set_current_page(2)
+            self.number_data_entry.grab_focus()
+        if self.reg_value.type == misc.REG_DWORD_BIG_ENDIAN:
+            self.type_notebook.set_current_page(2)
+            self.number_data_entry.grab_focus()
+        if self.reg_value.type == misc.REG_MULTI_SZ:
+            self.type_notebook.set_current_page(3)
+            self.multi_string_data_text_view.grab_focus()
+        if self.reg_value.type == misc.REG_QWORD:
+            self.type_notebook.set_current_page(2)
+            self.number_data_entry.grab_focus()
+        if self.brand_new:
+            self.name_entry.grab_focus()
+
+    def on_binary_data_hex_text_view_buffer_changed(self, widget):
+        if self.disable_signals:
+            return
+        self.disable_signals = True
+
+        hex_buffer = self.binary_data_hex_text_view.get_buffer() #this is the same as 'widget'
+        ascii_buffer = self.binary_data_ascii_text_view.get_buffer()
+        addr_buffer = self.binary_data_addr_text_view.get_buffer()
+
+        insert_iter = hex_buffer.get_iter_at_mark(hex_buffer.get_insert())
+        insert_char_offs = insert_iter.get_offset()
+        #print "cursor at:", insert_char_offs
+
+        text = hex_buffer.get_text(hex_buffer.get_start_iter(), hex_buffer.get_end_iter())
+        before_len = len(text)
+        text = RegValueEditDialog.check_hex_string(text).strip()
+        after_len = len(text)
+
+        hex_buffer.set_text(text)
+        ascii_buffer.set_text(RegValueEditDialog.hex_to_ascii(text))
+        addr_buffer.set_text(RegValueEditDialog.hex_to_addr(text))
+
+        #print "cursor now at:", insert_char_offs + (after_len - before_len)
+        hex_buffer.place_cursor(hex_buffer.get_iter_at_offset(insert_char_offs + self.hex_cursor_shift))
+        self.hex_cursor_shift = 0
+        self.disable_signals = False
+
+    def on_binary_data_hex_text_view_buffer_insert_text(self, widget, iter, text, length):
+        """callback for text inserted into the hex field. \nThe purpose of this function is only to update the cursor"""
+        if self.disable_signals:
+            return
+        self.disable_signals = True
+
+        offset = iter.get_offset()
+        whole_text = widget.get_text(widget.get_start_iter(), widget.get_end_iter())
+
+        #construct the final text
+        final_text = ""
+        for i in range(offset):
+            final_text += whole_text[i]
+        for ch in text:
+            final_text += ch
+        for i in range(offset, len(whole_text)):
+            final_text += whole_text[i]
+        final_text = self.check_hex_string(final_text)
+
+        #here we properly adjust the cursor
+        count = 0
+        limit = len(final_text) #it could be that the user typed an invalid character, so we'll play it safe
+        for i in range(offset, offset + length + count + 1): #go through the inserted characters and see if any have been replaced by white space
+            if (i < limit) and ((final_text[i] == ' ') or (final_text[i] == '\n')):
+                count += 1
+        self.hex_cursor_shift = count
+
+        self.disable_signals = False
+
+    def on_binary_data_hex_text_view_buffer_delete_range(self, widget, start, end):
+        """callback for text inserted into the hex field. \nThe purpose of this function is only to update the cursor"""
+        if (self.disable_signals):
+            return
+        self.disable_signals = True
+
+        text = widget.get_text(start, end)
+        if (text == ' ') or (text == '\n'):
+            #if the user deleted whitespace, they probably wanted to delete whatever was before it
+            new_start = widget.get_iter_at_offset(start.get_offset() - 1)
+            #widget.delete(new_start, start) #if this worked as expected, programming would be too easy :P
+
+        self.disable_signals = False
+
+    def on_binary_data_ascii_text_view_buffer_changed(self, widget):
+        """this function formats the text in the ascii field whenever it's changed"""
+        if (self.disable_signals):
+            return
+
+        self.disable_signals = True
+        if widget is None:
+            widget = self.binary_data_ascii_text_view.get_buffer()
+
+        #stuff we need to move the cursor properly later
+        cursor_iter = widget.get_iter_at_mark(widget.get_insert()) #insert means cursor, or "the insertion point" as gtk calls it
+        cursor_offset = cursor_iter.get_offset()
+
+        text = widget.get_text(widget.get_start_iter(), widget.get_end_iter())
+        text = self.check_ascii_string(text)
+        widget.set_text(text)
+
+        #Calling this function below will translate the hex back into our ascii box, making errors easier to spot
+        self.disable_signals = False
+        self.on_binary_data_hex_text_view_buffer_changed(None)
+        self.disable_signals = True
+
+        #now that we've overwritten everything in the textbuffer, we have to put the cursor back in the same spot
+        widget.place_cursor(widget.get_iter_at_offset(cursor_offset + self.ascii_cursor_shift))
+        self.ascii_cursor_shift = 0
+
+        self.disable_signals = False
+
+    def on_binary_data_ascii_text_view_buffer_insert_text(self, widget, iter, text, length):
+        if (self.disable_signals):
+            return
+        self.disable_signals = True
+        if widget is None:
+            widget = self.binary_data_ascii_text_view.get_buffer()
+
+        #get stuff that we need
+        offset = iter.get_offset()
+        inclusive_text = widget.get_text(widget.get_start_iter(), iter)
+        hex_pos = int(iter.get_offset() * 3) #because each ascii character is 2 hex characters, plus a space
+        hex_pos -= inclusive_text.count('\n') * 2 #because '\n' counts as a character, but it doesn't take up 3 spaces in the hex string.
+        hex_buffer = self.binary_data_hex_text_view.get_buffer()
+        addr_buffer = self.binary_data_addr_text_view.get_buffer()
+        hex_text = hex_buffer.get_text(hex_buffer.get_start_iter(), hex_buffer.get_end_iter())
+
+        #insert into hex_text up to the point where the new character was inserted
+        new_hex = ""
+        for ch in hex_text: #this works best because the hex_pos can be greater than len(hex_text) when inserting at the end
+            if len(new_hex) >= hex_pos:
+                break
+            new_hex += ch
+        #insert the new character(s)
+        for i in range(length):
+            new_hex += "%X" % ((ord(text[i]) >> 4) & 0x0F) #handle the upper 4 bits of the char
+            new_hex += "%X " % (ord(text[i]) & 0x0F)       #handle the lower 4 bits of the char
+        #insert the rest of the old characters into new_hex
+        while hex_pos < len(hex_text):
+            new_hex += hex_text[hex_pos]
+            hex_pos += 1
+        new_hex = self.check_hex_string(new_hex)
+
+        #here we properly adjust the cursor
+        final_text = self.hex_to_ascii(new_hex)
+        count = 0
+        for i in range(offset, offset + length + count): #go through the inserted characters and see if any have been replaced by '\n's
+            if (final_text[i] == '\n'):
+                count += 1
+        hex_buffer.set_text(new_hex) #set the text
+        self.ascii_cursor_shift = count #tell the on_changed() function to shift the cursor, since it has no effect if we call place_cursor() from here
+        addr_buffer.set_text(self.hex_to_addr(new_hex)) #can't forget to update the address text!
+        self.disable_signals = False
+
+    def on_binary_data_ascii_text_view_buffer_delete_range(self, widget, start, end):
+        if (self.disable_signals):
+            return
+        self.disable_signals = True
+
+        #get stuff that we need
+        text = widget.get_text(start, end)
+        beginning_text = widget.get_text(widget.get_start_iter(), start)
+        inclusive_text = widget.get_text(widget.get_start_iter(), end)
+        hex_buffer = self.binary_data_hex_text_view.get_buffer()
+        addr_buffer = self.binary_data_addr_text_view.get_buffer()
+        hex_text = hex_buffer.get_text(hex_buffer.get_start_iter(), hex_buffer.get_end_iter())
+        new_end_iter = None #this will tell us if any extra characters need to be deleted later
+
+        if text == '\n': #we assume that the user pressed backspace and NOT delete. We don't get any indicator of which key was pressed so without adding another complex function, this is the best we can do
+            new_end_iter = start #so later we can delete from a new start to the current start
+            start = widget.get_iter_at_offset(start.get_offset() - 1) #also delete the character before the '\n'
+
+        #Adjust the values for use in the hex field. this is basically the same as count('\n') + 1
+        hex_start = int(start.get_offset() * 3) #because each ascii character is 2 hex characters, plus a space
+        hex_start -= beginning_text.count('\n') * 2 #because '\n' counts as a character, but it doesn't take up 3 spaces in the hex string.
+        hex_end = int(end.get_offset() * 3)
+        hex_end -= inclusive_text.count('\n') * 2
+
+        new_hex = ""
+        #insert into hex_text up to the point where characters were deleted
+        for i in range(hex_start):
+            new_hex += hex_text[i]
+        #insert the characters after the deleted characters. We simply ignore the deleted characters
+        for i in range(hex_end, len(hex_text)):
+            new_hex += hex_text[i]
+
+        hex_buffer.set_text(RegValueEditDialog.check_hex_string(new_hex)) #set the text
+        addr_buffer.set_text(RegValueEditDialog.hex_to_addr(new_hex)) #can't forget to update the address text!
+
+        if (new_end_iter is not None):
+            widget.delete(start, new_end_iter) #this causes a warning because of bad iterators! Makes no sense
+
+        self.disable_signals = False
+
+    def on_binary_data_ascii_text_view_move_cursor(self, textview, step_size, count, extend_selection):
+        """This function handles cursor movement. For now it only responds to text selection"""
+        print "ext_sel", extend_selection
+        #The following doesn't work... even if extend_selection is true, get_selection_bounds() still returns nothing
+#        if (not extend_selection) or (self.disable_signals):
+#            return
+#        self.disable_signals = True
+#
+#        #get stuff we need
+#        ascii_buffer = textview.get_buffer()
+#        (start, end) = ascii_buffer.get_selection_bounds() #This function returns 2 iterators
+#        hex_buffer = self.binary_data_hex_text_view.get_buffer()
+#
+#        hex_start = int(start.get_offset() * 3) #because each ascii character is 2 hex characters, plus a space
+#        hex_start -= (hex_start/25) * 2 #because '\n' counts as a character, but it doesn't take up 3 spaces in the hex string.
+#        hex_end = int(end.get_offset() * 3)
+#        hex_end -= (hex_end/25) * 2
+#        hex_buffer.select_range(hex_buffer.get_iter_at_offset(hex_start), hex_buffer.get_iter_at_offset(hex_end))
+#
+#        self.disable_signals = False
+
+    def on_number_data_hex_radio_toggled(self, widget):
+        if (not widget.get_active()):
+            return
+
+        if (self.reg_value.type == misc.REG_QWORD):
+            digits = 16
+        else:
+            digits = 8
+
+        number_str = self.number_data_entry.get_text()
+        if (len(number_str) == 0):
+            number_str = "0"
+
+        number = string.atoi(number_str, 10)
+
+        format = "%0" + str(digits) + "X"
+
+        self.number_data_entry.set_text(format % number)
+
+    def on_number_data_dec_radio_toggled(self, widget):
+        if (not widget.get_active()):
+            return
+
+        if (self.reg_value.type == misc.REG_QWORD):
+            digits = 16
+        else:
+            digits = 8
+
+        number_str = self.number_data_entry.get_text()
+        if (len(number_str) == 0):
+            number_str = "0"
+
+        number = string.atoi(number_str, 0x10)
+
+        format = "%0" + str(digits) + "d"
+
+        self.number_data_entry.set_text(format % number)
+
+    def on_number_data_entry_changed(self, widget):
+        old_text = self.number_data_entry.get_text()
+
+        if (self.reg_value.type in [misc.REG_DWORD, misc.REG_DWORD_BIG_ENDIAN]):
+            max_len = 8
+        else:
+            max_len = 16
+
+        new_text = ""
+        if (self.number_data_hex_radio.get_active()):
+            for ch in old_text:
+                if (ch in string.hexdigits):
+                    new_text += ch
+            if (len(new_text) > max_len):
+                new_text = new_text[:max_len]
+
+        else:
+            for ch in old_text:
+                if (ch in string.digits):
+                    new_text += ch
+
+        self.number_data_entry.set_text(new_text)
+
+
+    @staticmethod
+    def remove_string_white_space(str):
+        return string.join(str.split(), "")
+
+    @staticmethod
+    def check_hex_string(old_string, line_length=8, remove_orphaned = False):
+        new_string = ""
+        length = 0
+        insert_space = False
+        for ch in old_string:
+            if (ch in string.hexdigits):
+                new_string += string.upper(ch)
+                if (insert_space):
+                    new_string += " "
+                    length += 1
+                insert_space = not insert_space
+
+            if (length >= line_length):
+                new_string += "\n"
+                length = 0
+
+        if (insert_space and remove_orphaned):
+            new_string = new_string.strip()[:len(new_string) - 1]
+
+        return new_string
+
+    @staticmethod
+    def check_ascii_string(string, line_length=8):
+        new_string = ""
+        digits = ""
+        length = 0
+        #insert a '\n' every line_length characters.
+        for ch in string:
+            if ch != '\n': #ignore carrage returns alreadys present
+                new_string += ch
+                length += 1
+                if (length >= line_length):
+                    new_string += "\n"
+                    length = 0
+
+        return new_string.strip()
+
+    @staticmethod
+    def hex_to_ascii(hex_string, line_length=8):
+        ascii_string = ""
+
+        digits = ""
+        length = 0
+        for ch in hex_string:
+            if (ch in string.hexdigits):
+                digits += ch
+
+            if (len(digits) >= 2):
+                new_chr = chr(string.atol(digits, 0x10))
+                if (new_chr in (string.punctuation + string.digits + string.ascii_letters + ' ')): #We don't just use string.printables because that inclues '\n' and '\r' which we don't want to put into the ascii box
+                    ascii_string += new_chr
+                else:
+                    ascii_string += "."
+                length += 1
+                digits = ""
+
+            if (length >= line_length):
+                ascii_string += "\n"
+                length = 0
+
+        return ascii_string
+
+    @staticmethod
+    def hex_to_addr(old_string, line_length=8):
+        new_string = ""
+
+        digits = ""
+        length = 0
+        addr = 0
+        for ch in old_string:
+            if (ch in string.hexdigits):
+                digits += ch
+
+            if (len(digits) >= 2):
+
+                if (length % line_length) == 0:
+                    new_string += "%04X\n" % addr
+                    addr += line_length
+
+                length += 1
+                digits = ""
+
+        return new_string
+
+    @staticmethod
+    def byte_array_to_hex(array, line_length=8):
+        new_string = ""
+
+        for byte in array:
+            new_string += "%02x" % byte
+
+        return RegValueEditDialog.check_hex_string(new_string, line_length, False)
+
+    @staticmethod
+    def hex_to_byte_array(hex_string):
+        array = []
+
+        digits = ""
+        for ch in hex_string:
+            if (ch in string.hexdigits):
+                digits += ch
+
+            if (len(digits) >= 2):
+                byte = string.atol(digits, 0x10)
+                array.append(byte)
+                digits = ""
+
+        return array
+
+
+class RegKeyEditDialog(gtk.Dialog):
+
+    def __init__(self, reg_key):
+        super(RegKeyEditDialog, self).__init__()
+
+        if (reg_key is None):
+            self.brand_new = True
+            self.reg_key = RegistryKey("", None)
+
+        else:
+            self.brand_new = False
+            self.reg_key = reg_key
+
+        self.create()
+        self.reg_key_to_values()
+
+    def create(self):
+        self.set_title(["Edit registry key", "New registry key"][self.brand_new])
+        self.set_border_width(5)
+
+        self.icon_registry_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.set_icon_from_file(self.icon_registry_filename)
+
+        self.set_resizable(False)
+
+
+        # value name
+
+        hbox = gtk.HBox()
+        self.vbox.pack_start(hbox, False, False, 10)
+
+        label = gtk.Label("Key name:")
+        hbox.pack_start(label, False, True, 10)
+
+        self.name_entry = gtk.Entry()
+        self.name_entry.set_activates_default(True)
+        hbox.pack_start(self.name_entry, True, True, 10)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new task
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+    def check_for_problems(self):
+        if (len(self.name_entry.get_text().strip()) == 0):
+            return "Please specify a name."
+
+        return None
+
+    def reg_key_to_values(self):
+        if (self.reg_key is None):
+            raise Exception("registry key not set")
+
+        self.name_entry.set_text(self.reg_key.name)
+
+    def values_to_reg_key(self):
+        if (self.reg_key is None):
+            raise Exception("registry key not set")
+
+        self.reg_key.name = self.name_entry.get_text()
+
+
+class RegRenameDialog(gtk.Dialog):
+
+    def __init__(self, reg_key, reg_value):
+        super(RegRenameDialog, self).__init__()
+
+        self.reg_key = reg_key
+        self.reg_value = reg_value
+
+        self.create()
+        self.reg_to_values()
+
+    def create(self):
+        self.set_title(["Rename registry key", "Rename registry value"][self.reg_value is not None])
+        self.set_border_width(5)
+
+        self.icon_registry_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.set_icon_from_file(self.icon_registry_filename)
+
+        self.set_resizable(False)
+
+
+        # name
+
+        hbox = gtk.HBox()
+        self.vbox.pack_start(hbox, False, False, 10)
+
+        label = gtk.Label("Name:")
+        hbox.pack_start(label, False, True, 10)
+
+        self.name_entry = gtk.Entry()
+        self.name_entry.set_activates_default(True)
+        hbox.pack_start(self.name_entry, True, True, 10)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+    def check_for_problems(self):
+        if (len(self.name_entry.get_text().strip()) == 0):
+            return "Please specify a name."
+        return None
+
+    def reg_to_values(self):
+        if (self.reg_key is None):
+            self.name_entry.set_text(self.reg_value.name)
+        else:
+            self.name_entry.set_text(self.reg_key.name)
+
+    def values_to_reg(self):
+        if (self.reg_key is None):
+            self.reg_value.name = self.name_entry.get_text()
+        else:
+            self.reg_key.name = self.name_entry.get_text()
+
+class RegSearchDialog(gtk.Dialog):
+
+    def __init__(self):
+        super(RegSearchDialog, self).__init__()
+
+        self.warned = False
+
+        self.create()
+
+    def create(self):
+        self.set_title("Search the registry")
+        self.set_border_width(5)
+
+        self.icon_registry_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.set_icon_from_file(self.icon_registry_filename)
+
+        self.set_resizable(False)
+
+
+        # name
+
+        hbox = gtk.HBox()
+        self.vbox.pack_start(hbox, False, False, 10)
+
+        label = gtk.Label("Search for: ")
+        hbox.pack_start(label, False, True, 10)
+
+        self.search_entry = gtk.Entry()
+        self.search_entry.set_activates_default(True)
+        hbox.pack_start(self.search_entry, True, True, 10)
+
+
+        # options
+
+        frame = gtk.Frame("Match:")
+        self.vbox.pack_start(frame, False, True, 0)
+
+        vbox = gtk.VBox()
+        vbox.set_border_width(4)
+        frame.add(vbox)
+
+        self.check_match_keys = gtk.CheckButton("Keys")
+        self.check_match_keys.set_active(True)
+        vbox.pack_start(self.check_match_keys, False, False, 0)
+        self.check_match_values = gtk.CheckButton("Values")
+        self.check_match_values.set_active(True)
+        vbox.pack_start(self.check_match_values, False, False, 0)
+        self.check_match_data = gtk.CheckButton("Data")
+        self.check_match_data.set_active(True)
+        vbox.pack_start(self.check_match_data, False, False, 0)
+
+        self.check_match_whole_string = gtk.CheckButton("Match whole string only")
+        self.vbox.pack_start(self.check_match_whole_string, False, False, 5)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.ok_button = gtk.Button("Search", gtk.STOCK_FIND)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+    def check_for_problems(self):
+        if self.search_entry.get_text() == "":
+            return ("You must enter text to search for!", gtk.MESSAGE_ERROR)
+        elif not self.check_match_data.get_active() and not self.check_match_keys.get_active() and not self.check_match_values.get_active():
+            return ("You much select at least one of: keys, values, or data to search", gtk.MESSAGE_ERROR)
+        elif not self.check_match_whole_string.get_active() and not self.warned:
+            for ch in self.search_entry.get_text():
+                if ch in string.punctuation:
+                    self.warned = True
+                    return ("Search items should be separated by a space. Punctuation (such as commas) will be considered part of the search string.\n\nPress find again to continue anyways.", gtk.MESSAGE_INFO)
+
+        return None
+
+class RegPermissionsDialog(gtk.Dialog):
+
+    def __init__(self, users, permissions):
+        super(RegPermissionsDialog, self).__init__()
+
+        self.users = users
+        self.permissions = permissions
+
+        self.create()
+
+        if users is not None:
+            for user in users:
+                self.user_store.append((user.username,
+                                        user,))
+
+    def create(self):
+        self.set_title("Permissions")
+        self.set_border_width(5)
+        self.set_resizable(True)
+        self.set_default_size(380, 480)
+
+        self.icon_registry_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.set_icon_from_file(self.icon_registry_filename)
+
+
+
+        # Groups/Users area
+
+        vbox = gtk.VBox()
+        self.vbox.pack_start(vbox, True, True, 10)
+
+        label = gtk.Label("Users:")
+        label.set_alignment(0, 1)
+        vbox.pack_start(label, False, False, 0)
+
+        hpaned = gtk.HPaned()
+        vbox.pack_start(hpaned, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hpaned.add1(scrolledwindow)
+
+        self.user_tree_view = gtk.TreeView()
+        self.user_tree_view.set_headers_visible(False)
+        scrolledwindow.add(self.user_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("User")
+        column.set_resizable(True)
+        column.set_fixed_width(200)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.user_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        self.user_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.user_tree_view.set_model(self.user_store)
+
+        hbox = gtk.HBox()
+        vbox.pack_start(hbox, False, False, 0)
+
+        padding = gtk.HBox()
+        hbox.pack_start(padding, True, True, 0)
+
+        self.add_button = gtk.Button("Add", gtk.STOCK_ADD)
+        hbox.pack_start(self.add_button, False, False, 2)
+
+        self.remove_button = gtk.Button("Remove", gtk.STOCK_REMOVE)
+        hbox.pack_start(self.remove_button, False, False, 2)
+
+
+
+        #Permissions area
+
+        vbox = gtk.VBox()
+        self.vbox.pack_start(vbox, True, True, 10)
+
+        self.permissions_label = gtk.Label("Permissions for UNKNOWN USER/GROUP:")
+        self.permissions_label.set_alignment(0, 1)
+        vbox.pack_start(self.permissions_label, False, False, 0)
+
+        hpaned = gtk.HPaned()
+        vbox.pack_start(hpaned, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hpaned.add1(scrolledwindow)
+
+        self.permissions_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.permissions_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Permission")
+        column.set_resizable(True)
+        column.set_min_width(160)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Allow")
+        column.set_resizable(False)
+        column.set_fixed_width(30)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererToggle()
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Deny")
+        column.set_resizable(False)
+        column.set_fixed_width(30)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererToggle()
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+
+        self.permissions_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT)
+        self.permissions_tree_view.set_model(self.permissions_store)
+
+        hbox = gtk.HBox()
+        vbox.pack_start(hbox, False, False, 0)
+
+        padding = gtk.HBox()
+        hbox.pack_start(padding, True, True, 0)
+
+        self.advanced_button = gtk.Button("Advanced")
+        hbox.pack_start(self.advanced_button, False, False, 2)
+
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.ok_button = gtk.Button("Ok", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.ok_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_APPLY)
+
+        self.set_default_response(gtk.RESPONSE_APPLY)
+
+
+        # signals/events
+
+        self.user_tree_view.get_selection().connect("changed", self.on_user_tree_view_selection_changed)
+        self.add_button.connect("clicked", self.on_add_item_activate)
+        self.remove_button.connect("clicked", self.on_remove_item_activate)
+
+        #TODO: permission view data editing updates the store?
+        self.advanced_button.connect("clicked", self.on_advanced_item_activate)
+
+    def check_for_problems(self):
+        #TODO: find problems?
+        return None
+
+    def on_user_tree_view_selection_changed(self, widget):
+        self.permissions_store.clear()
+
+        (iter, user) = self.get_selected_user()
+
+        if (iter is not None):
+            self.permissions_label.set_text("Permissions for " + user.username + ":")
+            #TODO: update permissions view on selection changed
+        else:
+            self.permissions_label.set_text("")
+
+    def on_add_item_activate(self, widget):
+        #TODO: implement add user for permissions
+        pass
+
+    def on_remove_item_activate(self, widget):
+        (iter, user) = self.get_selected_user()
+
+        if (iter is not None):
+            self.users.remove(user)
+            self.user_store.remove(iter)
+            #TODO: remove user permissions?
+
+    def on_advanced_item_activate(self, widget):
+        dialog = RegAdvancedPermissionsDialog(None, None)
+        dialog.show_all()
+        #TODO: handle advanced dialog
+        dialog.run()
+        dialog.hide_all()
+
+
+    def get_selected_user(self):
+        (model, iter) = self.user_tree_view.get_selection().get_selected()
+        if (iter is None): # no selection
+            return (None, None)
+        else:
+            return (iter, model.get_value(iter, 1))
+
+class RegAdvancedPermissionsDialog(gtk.Dialog):
+    def __init__(self, users, permissions):
+        super(RegAdvancedPermissionsDialog, self).__init__()
+
+        self.users = users
+        self.permissions = permissions
+
+        self.create()
+
+        self.insert_test_values() #TODO: remove
+
+        #update sensitivity
+        self.on_auditing_tree_view_selection_changed(None)
+        self.on_permissions_tree_view_selection_changed(None)
+
+    def create(self):
+        self.set_title("Permissions")
+        self.set_border_width(5)
+        self.set_resizable(True)
+        self.set_default_size(630, 490)
+
+        self.icon_registry_filename = os.path.join(sys.path[0], "images", "registry.png")
+        self.set_icon_from_file(self.icon_registry_filename)
+
+        self.notebook = gtk.Notebook()
+        self.vbox.pack_start(self.notebook, True, True, 0)
+
+
+
+        # Permissions tab
+
+        hbox = gtk.HBox() #hbox is for the padding on the left & right.
+        self.notebook.append_page(hbox, gtk.Label("Permissions"))
+
+        vbox = gtk.VBox()
+        hbox.pack_start(vbox, True, True, 10)
+
+        label = gtk.Label("To view the details of special permissions entries, select it and then click Edit.\n")
+        label.set_alignment(0, 0)
+        vbox.pack_start(label, False, False, 15)
+
+        label = gtk.Label("Permission entries:")
+        label.set_alignment(0, 1)
+        vbox.pack_start(label, False, False, 0)
+
+        hpaned = gtk.HPaned()
+        vbox.pack_start(hpaned, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hpaned.add1(scrolledwindow)
+
+        self.permissions_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.permissions_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Type")
+        column.set_resizable(True)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Permission")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Inherited from")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(3)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 3)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Applies to")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(4)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.permissions_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 4)
+
+        #Store contains: type (string), name (string), permission (string), inherited from (string), apply to (string, permissions (object)
+        self.permissions_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.permissions_tree_view.set_model(self.permissions_store)
+
+        hbox = gtk.HBox()
+        vbox.pack_start(hbox, False, False, 0)
+
+        padding = gtk.HBox()
+        hbox.pack_start(padding, True, True, 0)
+
+        self.add_button_permissions = gtk.Button("Add", gtk.STOCK_ADD)
+        hbox.pack_start(self.add_button_permissions, False, False, 2)
+
+        self.edit_button_permissions = gtk.Button("Edit", gtk.STOCK_EDIT)
+        hbox.pack_start(self.edit_button_permissions, False, False, 2)
+
+        self.remove_button_permissions = gtk.Button("Remove", gtk.STOCK_REMOVE)
+        hbox.pack_start(self.remove_button_permissions, False, False, 2)
+
+        check_area = gtk.VBox()
+        vbox.pack_start(check_area, False, False, 10)
+
+        self.check_inherit_permissions = gtk.CheckButton("Inherit permissions from parents that apply to child objects.")
+        check_area.pack_start(self.check_inherit_permissions, False, False, 0)
+
+        hbox = gtk.HBox()
+        check_area.pack_start(hbox, False, False, 0)
+
+        self.replace_child_permissions_button = gtk.Button("Replace child permissions")
+        hbox.pack_start(self.replace_child_permissions_button, False, False, 0)
+
+
+
+        # Auditing tab
+
+        hbox = gtk.HBox() #hbox is for the padding on the left & right.
+        self.notebook.append_page(hbox, gtk.Label("Auditing"))
+
+        vbox = gtk.VBox()
+        hbox.pack_start(vbox, True, True, 10)
+
+        label = gtk.Label("To view the details of special auditing entries, select it and then click Edit.\n")
+        label.set_alignment(0, 0)
+        vbox.pack_start(label, False, False, 15)
+
+        label = gtk.Label("Auditing entries:")
+        label.set_alignment(0, 1)
+        vbox.pack_start(label, False, False, 0)
+
+        hpaned = gtk.HPaned()
+        vbox.pack_start(hpaned, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hpaned.add1(scrolledwindow)
+
+        self.auditing_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.auditing_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Type")
+        column.set_resizable(True)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.auditing_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(1)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.auditing_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Permission")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(2)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.auditing_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 2)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Inherited from")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(3)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.auditing_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 3)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Applies to")
+        column.set_resizable(True)
+        column.set_expand(True)
+        column.set_sort_column_id(4)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.auditing_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 4)
+
+        self.auditing_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.auditing_tree_view.set_model(self.auditing_store)
+
+        hbox = gtk.HBox()
+        vbox.pack_start(hbox, False, False, 0)
+
+        padding = gtk.HBox()
+        hbox.pack_start(padding, True, True, 0)
+
+        self.add_button_auditing = gtk.Button("Add", gtk.STOCK_ADD)
+        hbox.pack_start(self.add_button_auditing, False, False, 2)
+
+        self.edit_button_auditing = gtk.Button("Edit", gtk.STOCK_EDIT)
+        hbox.pack_start(self.edit_button_auditing, False, False, 2)
+
+        self.remove_button_auditing = gtk.Button("Remove", gtk.STOCK_REMOVE)
+        hbox.pack_start(self.remove_button_auditing, False, False, 2)
+
+        check_area = gtk.VBox()
+        vbox.pack_start(check_area, False, False, 10)
+
+        self.check_inherit_auditing = gtk.CheckButton("Inherit auditing options from parents that apply to child objects.")
+        check_area.pack_start(self.check_inherit_auditing, False, False, 0)
+
+        hbox = gtk.HBox()
+        check_area.pack_start(hbox, False, False, 0)
+
+        self.replace_child_auditing_button = gtk.Button("Replace child auditing options")
+        hbox.pack_start(self.replace_child_auditing_button, False, False, 0)
+
+
+
+        # Ownership tab
+
+        hbox = gtk.HBox() #hbox is for the padding on the left & right.
+        self.notebook.append_page(hbox, gtk.Label("Ownership"))
+
+        vbox = gtk.VBox()
+        hbox.pack_start(vbox, True, True, 10)
+
+
+        label = gtk.Label("You may take ownership of an object if you have the appropriate permissions.\n")
+        label.set_alignment(0, 0)
+        vbox.pack_start(label, False, False, 15)
+
+        label = gtk.Label("Current owner of this item:")
+        label.set_alignment(0, 1)
+        vbox.pack_start(label, False, False, 0)
+
+        textview = gtk.Entry()
+        textview.set_editable(False)
+        vbox.pack_start(textview, False, False, 0)
+
+        label = gtk.Label("Change owner to:")
+        label.set_alignment(0, 1)
+        vbox.pack_start(label, False, False, 0)
+
+        hpaned = gtk.HPaned()
+        vbox.pack_start(hpaned, True, True, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hpaned.add1(scrolledwindow)
+
+        self.owner_tree_view = gtk.TreeView()
+        self.owner_tree_view.set_headers_visible(False)
+        scrolledwindow.add(self.owner_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Name")
+        column.set_resizable(True)
+        column.set_fixed_width(200)
+        column.set_sort_column_id(0)
+        renderer = gtk.CellRendererText()
+        renderer.set_property("ellipsize", pango.ELLIPSIZE_END)
+        column.pack_start(renderer, True)
+        self.owner_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        self.owner_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.owner_tree_view.set_model(self.owner_store)
+
+        self.check_replace_owner_child_objects = gtk.CheckButton("Replace ownership of child objects")
+        vbox.pack_start(self.check_replace_owner_child_objects, False, False, 10)
+
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.ok_button = gtk.Button("Ok", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.ok_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_APPLY)
+
+        self.set_default_response(gtk.RESPONSE_APPLY)
+
+        # TODO: Effective permissions
+
+        # signals/events
+        self.permissions_tree_view.get_selection().connect("changed", self.on_permissions_tree_view_selection_changed)
+        self.auditing_tree_view.get_selection().connect("changed", self.on_auditing_tree_view_selection_changed)
+
+        self.add_button_permissions.connect("clicked", self.on_add_permissions_button_clicked)
+        self.edit_button_permissions.connect("clicked", self.on_edit_permissions_button_clicked)
+        self.remove_button_permissions.connect("clicked", self.on_remove_permissions_button_clicked)
+        self.replace_child_permissions_button.connect("clicked", self.on_replace_permissions_button_clicked)
+        self.add_button_auditing.connect("clicked", self.on_add_auditing_button_clicked)
+        self.edit_button_auditing.connect("clicked", self.on_edit_auditing_button_clicked)
+        self.remove_button_auditing.connect("clicked", self.on_remove_auditing_button_clicked)
+        self.replace_child_auditing_button.connect("clicked", self.on_replace_auditing_button_clicked)
+
+        self.check_inherit_permissions.connect("clicked", self.on_check_inherit_permissions_changed)
+        self.check_inherit_auditing.connect("clicked", self.on_check_inherit_auditing_changed)
+
+
+
+    def insert_test_values(self):
+        #TODO: remove this function when no longer needed
+        self.check_inherit_permissions.set_active(True)
+        self.check_inherit_auditing.set_active(True)
+
+        user = User("Foo Bar", "", "", 0)
+        self.permissions_store.append(("Allow", user.username, "Special Permissions", "Unicorns", "This key only", user))
+        self.permissions_store.append(("Deny", "Pib", "Access to Playdoe", "HKEY_USERS", "This key and subkeys", user))
+
+        self.auditing_store.append(("Deny", "Homer Simpson", "Double Rainbow", "HKEY_LOCAL_MACHINE\\made up key\\temp\\new", "This key and subkeys", user))
+        self.auditing_store.append(("Allow", "Administrator", "Your Right to Party", "Earthworm Jim", "This key only", user))
+
+    def get_selected_permission(self):
+        (model, iter) = self.permissions_tree_view.get_selection().get_selected()
+        if (iter is None): # no selection
+            return (None, None)
+        else:
+            return (iter, model.get_value(iter, 1))
+
+    def get_selected_audit(self):
+        (model, iter) = self.auditing_tree_view.get_selection().get_selected()
+        if iter is None: # no selection
+            return (None, None)
+        else:
+            return (iter, model.get_value(iter, 1))
+
+    def on_permissions_tree_view_selection_changed(self, widget):
+        (iter, permission) = self.get_selected_permission()
+        self.edit_button_permissions.set_sensitive(permission is not None)
+        self.remove_button_permissions.set_sensitive(permission is not None)
+
+    def on_auditing_tree_view_selection_changed(self, widget):
+        (iter, audit) = self.get_selected_audit()
+        self.edit_button_auditing.set_sensitive(audit is not None)
+        self.remove_button_auditing.set_sensitive(audit is not None)
+
+    def on_add_permissions_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_edit_permissions_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_remove_permissions_button_clicked(self, widget):
+        (iter, permission) = self.get_selected_permission()
+        if (iter is not None):
+            self.permissions_store.remove(iter)
+
+    def on_replace_permissions_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_add_auditing_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_edit_auditing_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_remove_auditing_button_clicked(self, widget):
+        (iter, audit) = self.get_selected_audit()
+        if (iter is not None):
+            self.auditing_store.remove(iter)
+
+    def on_replace_auditing_button_clicked(self, widget):
+        #TODO: this
+        pass
+
+    def on_check_inherit_permissions_changed(self, widget):
+        if widget.get_active():
+            return
+        #TODO: if no permissions are inherited
+
+        message_box = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Unchecking this option means permissions inherited from the parent object will be lost.\n\nDo you want to copy the inherited permissions for this object?")
+        response = message_box.run()
+        message_box.hide()
+
+        if (response == gtk.RESPONSE_YES):
+            #TODO: copy permissions from the parent object
+            pass
+        elif (response == gtk.RESPONSE_NO):
+            #TODO: delete all inherited permissions from the permissions store
+            pass
+        else:#probably gtk.RESPONSE_DELETE_EVENT (from pressing escape)
+            widget.set_active(True)
+
+    def on_check_inherit_auditing_changed(self, widget):
+        if widget.get_active():
+            return
+        #TODO: if no permissions are inherited
+
+        message_box = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Unchecking this option means auditing options inherited from the parent object will be lost.\n\nDo you want to copy the inherited auditing options for this object?")
+        response = message_box.run()
+        message_box.hide()
+
+        if response == gtk.RESPONSE_YES:
+            #TODO: copy auditing from the parent object
+            pass
+        elif response == gtk.RESPONSE_NO:
+            #TODO: delete all inherited auditing from the permissions store
+            pass
+        else:#probably gtk.RESPONSE_DELETE_EVENT (from pressing escape)
+            widget.set_active(True)
+
+
+class WinRegConnectDialog(gtk.Dialog):
+
+    def __init__(self, server, transport_type, username, password=""):
+        super(WinRegConnectDialog, self).__init__()
+
+        self.server_address = server
+        self.transport_type = transport_type
+        self.username = username
+        self.password = password
+
+        self.create()
+
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title("Connect to a server")
+        self.set_border_width(5)
+        self.set_icon_name(gtk.STOCK_CONNECT)
+        self.set_resizable(False)
+
+
+        # server frame
+
+        self.vbox.set_spacing(5)
+
+        self.server_frame = gtk.Frame("Server")
+        self.vbox.pack_start(self.server_frame, False, True, 0)
+
+        table = gtk.Table(3, 2)
+        table.set_border_width(5)
+        self.server_frame.add(table)
+
+        label = gtk.Label(" Server address: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.server_address_entry = gtk.Entry()
+        self.server_address_entry.set_text(self.server_address)
+        self.server_address_entry.set_activates_default(True)
+        table.attach(self.server_address_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Username: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.username_entry = gtk.Entry()
+        self.username_entry.set_text(self.username)
+        self.username_entry.set_activates_default(True)
+        table.attach(self.username_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Password: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_text(self.password)
+        self.password_entry.set_visibility(False)
+        self.password_entry.set_activates_default(True)
+        table.attach(self.password_entry, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+
+        # transport frame
+
+        self.transport_frame = gtk.Frame(" Transport type ")
+        self.vbox.pack_start(self.transport_frame, False, True, 0)
+
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.transport_frame.add(vbox)
+
+        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP")
+        self.rpc_smb_tcpip_radio_button.set_active(self.transport_type == 0)
+        vbox.pack_start(self.rpc_smb_tcpip_radio_button)
+
+        self.rpc_tcpip_radio_button = gtk.RadioButton(self.rpc_smb_tcpip_radio_button, "RPC over TCP/IP")
+        self.rpc_tcpip_radio_button.set_active(self.transport_type == 1)
+        vbox.pack_start(self.rpc_tcpip_radio_button)
+
+        self.localhost_radio_button = gtk.RadioButton(self.rpc_tcpip_radio_button, "Localhost")
+        self.localhost_radio_button.set_active(self.transport_type == 2)
+        vbox.pack_start(self.localhost_radio_button)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.connect_button = gtk.Button("Connect", gtk.STOCK_CONNECT)
+        self.connect_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.connect_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.rpc_smb_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.rpc_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.localhost_radio_button.connect("toggled", self.on_radio_button_toggled)
+
+    def update_sensitivity(self):
+        server_required = not self.localhost_radio_button.get_active()
+
+        self.server_address_entry.set_sensitive(server_required)
+
+    def get_server_address(self):
+        return self.server_address_entry.get_text().strip()
+
+    def get_transport_type(self):
+        if self.rpc_smb_tcpip_radio_button.get_active():
+            return 0
+        elif self.rpc_tcpip_radio_button.get_active():
+            return 1
+        elif self.localhost_radio_button.get_active():
+            return 2
+        else:
+            return -1
+
+    def get_username(self):
+        return self.username_entry.get_text().strip()
+
+    def get_password(self):
+        return self.password_entry.get_text()
+
+    def on_radio_button_toggled(self, widget):
+        self.update_sensitivity()
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/sam.py b/build/lib.linux-x86_64-2.7/sambagtk/sam.py
new file mode 100644 (file)
index 0000000..84b986a
--- /dev/null
@@ -0,0 +1,688 @@
+# Samba GTK+ frontends
+#
+# Copyright (C) 2010 Sergio Martins <sergio97@gmail.com>
+# Copyright (C) 2011 Jelmer Vernooij <jelmer@samba.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+"""SAM-related dialogs."""
+
+import gtk
+import gobject
+
+import os
+import sys
+
+
+class User(object):
+
+    def __init__(self, username, fullname, description, rid):
+        self.username = username
+        self.fullname = fullname
+        self.description = description
+        self.rid = rid
+
+        self.password = ""
+        self.must_change_password = True
+        self.cannot_change_password = False
+        self.password_never_expires = False
+        self.account_disabled = False
+        self.account_locked_out = False
+        self.group_list = []
+        self.profile_path = ""
+        self.logon_script = ""
+        self.homedir_path = ""
+        self.map_homedir_drive = -1
+
+    def list_view_representation(self):
+        return [self.username, self.fullname, self.description, self.rid]
+
+
+class Group(object):
+
+    def __init__(self, name, description, rid):
+        self.name = name
+        self.description = description
+        self.rid = rid
+
+    def list_view_representation(self):
+        return [self.name, self.description, self.rid]
+
+
+class UserEditDialog(gtk.Dialog):
+
+    def __init__(self, pipe_manager, user=None):
+        super(UserEditDialog, self).__init__()
+
+        if (user is None):
+            self.brand_new = True
+            self.user = User("", "", "", 0)
+        else:
+            self.brand_new = False
+            self.user = user
+
+        self.pipe_manager = pipe_manager
+        self.create()
+
+        self.user_to_values()
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title(["Edit user", "New user"][self.brand_new] + " " + self.user.username)
+        self.set_border_width(5)
+        self.set_icon_from_file(os.path.join(sys.path[0], "images", "user.png"))
+
+        notebook = gtk.Notebook()
+        self.vbox.pack_start(notebook, True, True, 0)
+
+        table = gtk.Table (10, 2, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        notebook.add(table)
+
+        label = gtk.Label("Username")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Full name")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Description")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Password")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 3, 4, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Confirm password")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 4, 5, gtk.FILL, 0, 0, 0)
+
+        self.username_entry = gtk.Entry()
+        self.username_entry.set_activates_default(True)
+        self.username_entry.set_max_length(20) #This is the length limit for usernames
+        table.attach(self.username_entry, 1, 2, 0, 1, gtk.FILL, 0, 0, 0)
+
+        self.fullname_entry = gtk.Entry()
+        self.fullname_entry.set_activates_default(True)
+        table.attach(self.fullname_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.description_entry = gtk.Entry()
+        self.description_entry.set_activates_default(True)
+        table.attach(self.description_entry, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_visibility(False)
+        self.password_entry.set_activates_default(True)
+        table.attach(self.password_entry, 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.confirm_password_entry = gtk.Entry()
+        self.confirm_password_entry.set_visibility(False)
+        self.confirm_password_entry.set_activates_default(True)
+        table.attach(self.confirm_password_entry, 1, 2, 4, 5, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.must_change_password_check = gtk.CheckButton("_User Must Change Password at Next Logon")
+        self.must_change_password_check.set_active(self.brand_new)
+        table.attach(self.must_change_password_check, 1, 2, 5, 6, gtk.FILL, 0, 0, 0)
+
+        self.cannot_change_password_check = gtk.CheckButton("User Cannot Change Password")
+        table.attach(self.cannot_change_password_check, 1, 2, 6, 7, gtk.FILL, 0, 0, 0)
+
+        self.password_never_expires_check = gtk.CheckButton("Password Never Expires")
+        table.attach(self.password_never_expires_check, 1, 2, 7, 8, gtk.FILL, 0, 0, 0)
+
+        self.account_disabled_check = gtk.CheckButton("Account Disabled")
+        self.account_disabled_check.set_active(self.brand_new)
+        table.attach(self.account_disabled_check, 1, 2, 8, 9, gtk.FILL, 0, 0, 0)
+
+        self.account_locked_out_check = gtk.CheckButton("Account Locked Out")
+        table.attach(self.account_locked_out_check, 1, 2, 9, 10, gtk.FILL, 0, 0, 0)
+
+        notebook.set_tab_label(notebook.get_nth_page(0), gtk.Label("Main"))
+
+        hbox = gtk.HBox(False, 5)
+        notebook.add(hbox)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hbox.pack_start(scrolledwindow, True, True, 0)
+
+        self.existing_groups_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.existing_groups_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Existing groups")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.existing_groups_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        self.existing_groups_store = gtk.ListStore(gobject.TYPE_STRING)
+        self.existing_groups_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+        self.existing_groups_tree_view.set_model(self.existing_groups_store)
+
+        vbox = gtk.VBox(True, 0)
+        hbox.pack_start(vbox, True, True, 0)
+
+        self.add_group_button = gtk.Button("Add", gtk.STOCK_ADD)
+        vbox.pack_start(self.add_group_button, False, False, 0)
+
+        self.del_group_button = gtk.Button("Remove", gtk.STOCK_REMOVE)
+        vbox.pack_start(self.del_group_button, False, False, 0)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        hbox.pack_start(scrolledwindow, True, True, 0)
+
+        self.available_groups_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.available_groups_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Available groups")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.available_groups_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        self.available_groups_store = gtk.ListStore(gobject.TYPE_STRING)
+        self.available_groups_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+        self.available_groups_tree_view.set_model(self.available_groups_store)
+
+        notebook.set_tab_label(notebook.get_nth_page(1), gtk.Label("Groups"))
+
+        vbox = gtk.VBox(False, 0)
+        notebook.add(vbox)
+
+        frame = gtk.Frame("User Profiles")
+        frame.set_border_width(5)
+        vbox.pack_start(frame, True, True, 0)
+
+        table = gtk.Table(2, 2, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        frame.add(table)
+
+        label = gtk.Label("User Profile Path")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Logon Script Name")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, 0, 0, 0)
+
+        self.profile_path_entry = gtk.Entry()
+        self.profile_path_entry.set_activates_default(True)
+        table.attach(self.profile_path_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.logon_script_entry = gtk.Entry()
+        self.logon_script_entry.set_activates_default(True)
+        table.attach(self.logon_script_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        frame = gtk.Frame("Home Directory")
+        frame.set_border_width(5)
+        vbox.pack_start(frame, True, True, 0)
+
+        table = gtk.Table(2, 2, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        frame.add(table)
+
+        label = gtk.Label("Path")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, 0, 0, 0)
+
+        self.homedir_path_entry = gtk.Entry()
+        self.homedir_path_entry.set_activates_default(True)
+        table.attach(self.homedir_path_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.map_homedir_drive_check = gtk.CheckButton("Map homedir to drive")
+        table.attach(self.map_homedir_drive_check, 0, 1, 1, 2, gtk.FILL, 0, 0, 0)
+
+        self.map_homedir_drive_combo = gtk.combo_box_new_text()
+        table.attach(self.map_homedir_drive_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 0, 0)
+
+        for i in range(ord('Z') - ord('A') + 1):
+            self.map_homedir_drive_combo.append_text(chr(i + ord('A')) + ':')
+
+        notebook.set_tab_label(notebook.get_nth_page(2), gtk.Label("Profile"))
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new user
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.must_change_password_check.connect("toggled", self.on_update_sensitivity)
+        self.cannot_change_password_check.connect("toggled", self.on_update_sensitivity)
+        self.password_never_expires_check.connect("toggled", self.on_update_sensitivity)
+        self.account_disabled_check.connect("toggled", self.on_update_sensitivity)
+        self.account_locked_out_check.connect("toggled", self.on_update_sensitivity)
+
+        self.add_group_button.connect("clicked", self.on_add_group_button_clicked)
+        self.del_group_button.connect("clicked", self.on_del_group_button_clicked)
+        self.existing_groups_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.available_groups_tree_view.get_selection().connect("changed", self.on_update_sensitivity)
+        self.map_homedir_drive_check.connect("toggled", self.on_update_sensitivity)
+
+    def check_for_problems(self):
+        if (self.password_entry.get_text() != self.confirm_password_entry.get_text()):
+            return "The password was not correctly confirmed. Please ensure that the password and confirmation match exactly."
+
+        if len(self.username_entry.get_text()) == 0:
+            return "Username may not be empty!"
+
+        if self.brand_new:
+            for user in self.pipe_manager.user_list:
+                if user.username == self.username_entry.get_text():
+                    return "User \"" + user.username + "\" already exists!"
+
+        return None
+
+    def update_sensitivity(self):
+        existing_selected = (self.existing_groups_tree_view.get_selection().count_selected_rows() > 0)
+        available_selected = (self.available_groups_tree_view.get_selection().count_selected_rows() > 0)
+
+        if (self.password_never_expires_check.get_active() or
+            self.cannot_change_password_check.get_active()):
+            self.must_change_password_check.set_sensitive(False)
+        else:
+            self.must_change_password_check.set_sensitive(True)
+        self.cannot_change_password_check.set_sensitive(not self.must_change_password_check.get_active())
+        self.password_never_expires_check.set_sensitive(not self.must_change_password_check.get_active())
+
+        # It is possible that many of these options are turned on at the same
+        # time, even though they shouldn't be
+        if self.must_change_password_check.get_active():
+            self.must_change_password_check.set_sensitive(True)
+        if self.password_never_expires_check.get_active():
+            self.password_never_expires_check.set_sensitive(True)
+        if self.cannot_change_password_check.get_active():
+            self.cannot_change_password_check.set_sensitive(True)
+
+        self.add_group_button.set_sensitive(available_selected)
+        self.del_group_button.set_sensitive(existing_selected)
+
+        self.map_homedir_drive_combo.set_sensitive(self.map_homedir_drive_check.get_active())
+
+    def user_to_values(self):
+        if self.user is None:
+            raise Exception("user not set")
+
+        self.username_entry.set_text(self.user.username)
+        self.username_entry.set_sensitive(len(self.user.username) == 0)
+        self.fullname_entry.set_text(self.user.fullname)
+        self.description_entry.set_text(self.user.description)
+        self.must_change_password_check.set_active(self.user.must_change_password)
+        self.cannot_change_password_check.set_active(self.user.cannot_change_password)
+        self.password_never_expires_check.set_active(self.user.password_never_expires)
+        self.account_disabled_check.set_active(self.user.account_disabled)
+        self.account_locked_out_check.set_active(self.user.account_locked_out)
+        self.profile_path_entry.set_text(self.user.profile_path)
+        self.logon_script_entry.set_text(self.user.logon_script)
+        self.homedir_path_entry.set_text(self.user.homedir_path)
+
+        if (self.user.map_homedir_drive != -1):
+            self.map_homedir_drive_check.set_active(True)
+            self.map_homedir_drive_combo.set_active(self.user.map_homedir_drive)
+            self.map_homedir_drive_combo.set_sensitive(True)
+        else:
+            self.map_homedir_drive_check.set_active(False)
+            self.map_homedir_drive_combo.set_active(-1)
+            self.map_homedir_drive_combo.set_sensitive(False)
+
+        self.existing_groups_store.clear()
+        for group in self.user.group_list:
+            self.existing_groups_store.append([group.name])
+
+        self.available_groups_store.clear()
+        for group in self.pipe_manager.group_list:
+            if (not group in self.user.group_list):
+                self.available_groups_store.append([group.name])
+
+    def values_to_user(self):
+        if self.user is None:
+            raise Exception("user not set")
+
+        self.user.username = self.username_entry.get_text()
+        self.user.fullname = self.fullname_entry.get_text()
+        self.user.description = self.description_entry.get_text()
+        self.user.password = (None, self.password_entry.get_text())[len(self.password_entry.get_text()) > 0]
+        self.user.must_change_password = self.must_change_password_check.get_active()
+        self.user.cannot_change_password = self.cannot_change_password_check.get_active()
+        self.user.password_never_expires = self.password_never_expires_check.get_active()
+        self.user.account_disabled = self.account_disabled_check.get_active()
+        self.user.account_locked_out = self.account_locked_out_check.get_active()
+        self.user.profile_path = self.profile_path_entry.get_text()
+        self.user.logon_script = self.logon_script_entry.get_text()
+        self.user.homedir_path = self.homedir_path_entry.get_text()
+
+        if (self.map_homedir_drive_check.get_active()) and (self.map_homedir_drive_combo.get_active() != -1):
+            self.user.map_homedir_drive = self.map_homedir_drive_combo.get_active()
+        else:
+            self.user.map_homedir_drive = -1
+
+        del self.user.group_list[:]
+
+        iter = self.existing_groups_store.get_iter_first()
+        while (iter is not None):
+            value = self.existing_groups_store.get_value(iter, 0)
+            self.user.group_list.append([group for group in self.pipe_manager.group_list if group.name == value][0])
+            iter = self.existing_groups_store.iter_next(iter)
+
+    def on_add_group_button_clicked(self, widget):
+        (model, iter) = self.available_groups_tree_view.get_selection().get_selected()
+        if (iter is None):
+            return
+
+        group_name = model.get_value(iter, 0)
+        self.existing_groups_store.append([group_name])
+        self.available_groups_store.remove(iter)
+
+    def on_del_group_button_clicked(self, widget):
+        (model, iter) = self.existing_groups_tree_view.get_selection().get_selected()
+        if (iter is None):
+            return
+
+        group_name = model.get_value(iter, 0)
+        self.available_groups_store.append([group_name])
+        self.existing_groups_store.remove(iter)
+
+    def on_update_sensitivity(self, widget):
+        self.update_sensitivity()
+
+
+class GroupEditDialog(gtk.Dialog):
+
+    def __init__(self, pipe_manager, group = None):
+        super(GroupEditDialog, self).__init__()
+
+        if group is None:
+            self.brand_new = True
+            self.thegroup = Group("", "", 0)
+        else:
+            self.brand_new = False
+            self.thegroup = group
+
+        self.pipe_manager = pipe_manager
+        self.create()
+
+        if not self.brand_new:
+            self.group_to_values()
+
+    def create(self):
+        self.set_title(["Edit group", "New group"][self.brand_new] + " " + self.thegroup.name)
+        self.set_border_width(5)
+        self.set_icon_from_file(os.path.join(sys.path[0], "images", "group.png"))
+
+        table = gtk.Table (2, 2, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        self.vbox.pack_start(table, True, True, 0)
+
+        label = gtk.Label("Name")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Description")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, 0, 0, 0)
+
+        self.name_entry = gtk.Entry()
+        self.name_entry.set_activates_default(True)
+        table.attach(self.name_entry, 1, 2, 0, 1, gtk.FILL, 0, 0, 0)
+
+        self.description_entry = gtk.Entry()
+        self.description_entry.set_activates_default(True)
+        table.attach(self.description_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, 0, 0, 0)
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new group
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+    def check_for_problems(self):
+        if len(self.name_entry.get_text()) == 0:
+            return "Name may not be empty!"
+
+        if self.brand_new:
+            for group in self.pipe_manager.group_list:
+                if group.name == self.name_entry.get_text():
+                    return "Choose another group name, this one already exists!"
+
+        return None
+
+    def group_to_values(self):
+        if (self.thegroup is None):
+            raise Exception("group not set")
+
+        self.name_entry.set_text(self.thegroup.name)
+        self.name_entry.set_sensitive(len(self.thegroup.name) == 0)
+        self.description_entry.set_text(self.thegroup.description)
+
+    def values_to_group(self):
+        if self.thegroup is None:
+            raise Exception("group not set")
+
+        self.thegroup.name = self.name_entry.get_text()
+        self.thegroup.description = self.description_entry.get_text()
+
+
+class SAMConnectDialog(gtk.Dialog):
+
+    def __init__(self, server, transport_type, username, password = ""):
+        super(SAMConnectDialog, self).__init__()
+
+        self.server_address = server
+        self.transport_type = transport_type
+        self.username = username
+        self.password = password
+        self.domains = None
+
+        self.create()
+
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title("Connect to SAM server")
+        self.set_border_width(5)
+        self.set_icon_name(gtk.STOCK_CONNECT)
+        self.set_resizable(False)
+
+        # server frame
+
+        self.vbox.set_spacing(5)
+
+        self.server_frame = gtk.Frame("Server")
+        self.vbox.pack_start(self.server_frame, False, True, 0)
+
+        table = gtk.Table(3, 2)
+        table.set_border_width(5)
+        self.server_frame.add(table)
+
+        label = gtk.Label(" Server address: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.server_address_entry = gtk.Entry()
+        self.server_address_entry.set_text(self.server_address)
+        self.server_address_entry.set_activates_default(True)
+        table.attach(self.server_address_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Username: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.username_entry = gtk.Entry()
+        self.username_entry.set_text(self.username)
+        self.username_entry.set_activates_default(True)
+        table.attach(self.username_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Password: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_text(self.password)
+        self.password_entry.set_visibility(False)
+        self.password_entry.set_activates_default(True)
+        table.attach(self.password_entry, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+
+        # transport frame
+
+        self.transport_frame = gtk.Frame(" Transport type ")
+        self.vbox.pack_start(self.transport_frame, False, True, 0)
+
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.transport_frame.add(vbox)
+
+        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP")
+        self.rpc_smb_tcpip_radio_button.set_active(self.transport_type == 0)
+        vbox.pack_start(self.rpc_smb_tcpip_radio_button)
+
+        self.rpc_tcpip_radio_button = gtk.RadioButton(self.rpc_smb_tcpip_radio_button, "RPC over TCP/IP")
+        self.rpc_tcpip_radio_button.set_active(self.transport_type == 1)
+        vbox.pack_start(self.rpc_tcpip_radio_button)
+
+        self.localhost_radio_button = gtk.RadioButton(self.rpc_tcpip_radio_button, "Localhost")
+        self.localhost_radio_button.set_active(self.transport_type == 2)
+        vbox.pack_start(self.localhost_radio_button)
+
+
+        # domain frame
+
+        self.domains_frame = gtk.Frame(" Domain ")
+        self.domains_frame.set_no_show_all(True)
+        self.vbox.pack_start(self.domains_frame, False, True, 0)
+
+        table = gtk.Table(1, 2)
+        table.set_border_width(5)
+        self.domains_frame.add(table)
+
+        label = gtk.Label("Select domain: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.domain_combo_box = gtk.combo_box_new_text()
+        table.attach(self.domain_combo_box, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.connect_button = gtk.Button("Connect", gtk.STOCK_CONNECT)
+        self.connect_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.connect_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.rpc_smb_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.rpc_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.localhost_radio_button.connect("toggled", self.on_radio_button_toggled)
+
+    def update_sensitivity(self):
+        server_required = not self.localhost_radio_button.get_active()
+
+        self.server_address_entry.set_sensitive(server_required)
+
+    def set_domains(self, domains, domain_index=-1):
+        if domains is not None:
+            self.server_frame.set_sensitive(False)
+            self.transport_frame.set_sensitive(False)
+
+            self.domains_frame.set_no_show_all(False)
+            self.domains_frame.show_all()
+            self.domains_frame.set_no_show_all(True)
+            self.domain_combo_box.get_model().clear()
+            for domain in domains:
+                self.domain_combo_box.append_text(domain)
+
+            if domain_index != -1:
+                self.domain_combo_box.set_active(domain_index)
+        else:
+            self.server_frame.set_sensitive(True)
+            self.transport_frame.set_sensitive(True)
+            self.domains_frame.hide_all()
+
+    def get_server_address(self):
+        return self.server_address_entry.get_text().strip()
+
+    def get_transport_type(self):
+        if self.rpc_smb_tcpip_radio_button.get_active():
+            return 0
+        elif self.rpc_tcpip_radio_button.get_active():
+            return 1
+        elif self.localhost_radio_button.get_active():
+            return 2
+        else:
+            return -1
+
+    def get_username(self):
+        return self.username_entry.get_text().strip()
+
+    def get_password(self):
+        return self.password_entry.get_text()
+
+    def get_domain_index(self):
+        return self.domain_combo_box.get_active()
+
+    def on_radio_button_toggled(self, widget):
+        self.update_sensitivity()
+
+
+
diff --git a/build/lib.linux-x86_64-2.7/sambagtk/svcctl.py b/build/lib.linux-x86_64-2.7/sambagtk/svcctl.py
new file mode 100644 (file)
index 0000000..6f44235
--- /dev/null
@@ -0,0 +1,614 @@
+# Samba GTK+ frontends
+#
+# Copyright (C) 2010 Sergio Martins <sergio97@gmail.com>
+# Copyright (C) 2011 Jelmer Vernooij <jelmer@samba.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+"""svcctl related dialogs."""
+
+import gobject
+import gtk
+import os
+import pango
+import sys
+
+from samba.dcerpc import svcctl
+
+class Service(object):
+
+    def __init__(self):
+        self.name = ""
+        self.display_name = ""
+        self.description = ""
+
+        self.state = svcctl.SVCCTL_STOPPED
+        self.check_point = 0
+        self.wait_hint = 0
+
+        self.accepts_pause = False
+        self.accepts_stop = False
+
+        self.start_type = svcctl.SVCCTL_AUTO_START
+        self.path_to_exe = ""
+        self.account = None # local system account
+        self.account_password = None # don't change
+        self.allow_desktop_interaction = False
+
+        self.start_params = ""
+        #self.hw_profile_list = [["Profile 1", True], ["Profile 2", False]] TODO: implement hw_profiles functionality
+
+        self.handle = -1
+
+    @staticmethod
+    def get_state_string(state):
+        return {
+            svcctl.SVCCTL_CONTINUE_PENDING: "Continue pending",
+            svcctl.SVCCTL_PAUSE_PENDING: "Pause pending",
+            svcctl.SVCCTL_PAUSED: "Paused",
+            svcctl.SVCCTL_RUNNING: "Running",
+            svcctl.SVCCTL_START_PENDING: "Start pending",
+            svcctl.SVCCTL_STOP_PENDING: "Stop pending",
+            svcctl.SVCCTL_STOPPED: "Stopped"
+            }[state]
+
+    @staticmethod
+    def get_start_type_string(start_type):
+        return {
+            svcctl.SVCCTL_BOOT_START: "Start at boot",
+            svcctl.SVCCTL_SYSTEM_START: "Start at system startup",
+            svcctl.SVCCTL_AUTO_START: "Start automatically",
+            svcctl.SVCCTL_DEMAND_START: "Start manually",
+            svcctl.SVCCTL_DISABLED: "Disabled",
+            }.get(start_type, "")
+
+    def list_view_representation(self):
+        return [self.name, self.display_name, self.description,
+                Service.get_state_string(self.state),
+                Service.get_start_type_string(self.start_type)]
+
+
+class ServiceEditDialog(gtk.Dialog):
+
+    def __init__(self, service = None):
+        super(ServiceEditDialog, self).__init__()
+
+        if (service is None):
+            self.brand_new = True
+            self.service = Service()
+        else:
+            self.brand_new = False
+            self.service = service
+
+        self.create()
+
+        if (not self.brand_new):
+            self.service_to_values()
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title("Edit service " + self.service.name)
+        self.set_border_width(5)
+        self.icon_pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(sys.path[0], "images", "service.png"))
+        self.set_icon(self.icon_pixbuf)
+        self.set_resizable(False)
+        self.set_size_request(450, 350)
+
+        notebook = gtk.Notebook()
+        self.vbox.pack_start(notebook, True, True, 0)
+
+
+        # general tab
+
+        table = gtk.Table(6, 2, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        notebook.add(table)
+
+        label = gtk.Label("Name")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, 0, 5, 0)
+
+        label = gtk.Label("Display name")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, 0, 5, 0)
+
+        label = gtk.Label("Description")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 5, 0)
+
+        label = gtk.Label("Path to executable")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 3, 4, gtk.FILL, 0, 5, 0)
+
+        label = gtk.Label("Startup type")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 4, 5, gtk.FILL, 0, 5, 0)
+
+        label = gtk.Label("Start parameters")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 5, 6, gtk.FILL, 0, 5, 0)
+
+        self.name_label = gtk.Label()
+        self.name_label.set_alignment(0, 0.5)
+        table.attach(self.name_label, 1, 2, 0, 1, gtk.FILL, 0, 0, 5)
+
+        self.display_name_entry = gtk.Entry()
+        self.display_name_entry.set_editable(False)
+        table.attach(self.display_name_entry, 1, 2, 1, 2, gtk.FILL, 0, 0, 5)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        scrolledwindow.set_size_request(0, 50)
+        table.attach(scrolledwindow, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 5)
+
+        self.description_text_view = gtk.TextView()
+        self.description_text_view.set_editable(False)
+        self.description_text_view.set_wrap_mode(gtk.WRAP_WORD)
+        scrolledwindow.add(self.description_text_view)
+
+        self.exe_path_entry = gtk.Entry()
+        self.exe_path_entry.set_editable(False)
+        table.attach(self.exe_path_entry, 1, 2, 3, 4, gtk.FILL, 0, 0, 0)
+
+        self.startup_type_combo = gtk.combo_box_new_text()
+        self.startup_type_combo.append_text(Service.get_start_type_string(svcctl.SVCCTL_BOOT_START))
+        self.startup_type_combo.append_text(Service.get_start_type_string(svcctl.SVCCTL_SYSTEM_START))
+        self.startup_type_combo.append_text(Service.get_start_type_string(svcctl.SVCCTL_AUTO_START))
+        self.startup_type_combo.append_text(Service.get_start_type_string(svcctl.SVCCTL_DEMAND_START))
+        self.startup_type_combo.append_text(Service.get_start_type_string(svcctl.SVCCTL_DISABLED))
+        table.attach(self.startup_type_combo, 1, 2, 4, 5, gtk.FILL, 0, 0, 0)
+
+        self.start_params_entry = gtk.Entry()
+        self.start_params_entry.set_activates_default(True)
+        table.attach(self.start_params_entry, 1, 2, 5, 6, gtk.FILL, 0, 0, 0)
+
+        notebook.set_tab_label(notebook.get_nth_page(0), gtk.Label("General"))
+
+
+        # log on tab
+
+        table = gtk.Table(8, 3, False)
+        table.set_border_width(5)
+        table.set_col_spacings(5)
+        table.set_row_spacings(5)
+        notebook.add(table)
+
+        self.local_account_radio = gtk.RadioButton(None, "_Local System account")
+        table.attach(self.local_account_radio, 0, 1, 0, 1, gtk.FILL, 0, 0, 0)
+
+        self.allow_desktop_interaction_check = gtk.CheckButton("Allo_w service to interact with desktop")
+        table.attach(self.allow_desktop_interaction_check, 0, 2, 1, 2, gtk.FILL, 0, 20, 0)
+
+        self.this_account_radio = gtk.RadioButton(self.local_account_radio, "_This account:")
+        table.attach(self.this_account_radio, 0, 1, 2, 3, gtk.FILL, 0, 0, 0)
+
+        self.account_entry = gtk.Entry()
+        self.account_entry.set_activates_default(True)
+        table.attach(self.account_entry, 1, 2, 2, 3, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Password:")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 3, 4, gtk.FILL, 0, 20, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_activates_default(True)
+        self.password_entry.set_visibility(False)
+        table.attach(self.password_entry, 1, 2, 3, 4, gtk.FILL, 0, 0, 0)
+
+        label = gtk.Label("Confirm password:")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 4, 5, gtk.FILL, 0, 20, 0)
+
+        self.confirm_password_entry = gtk.Entry()
+        self.confirm_password_entry.set_activates_default(True)
+        self.confirm_password_entry.set_visibility(False)
+        table.attach(self.confirm_password_entry, 1, 2, 4, 5, gtk.FILL, 0, 0, 0)
+
+
+        # TODO: implement hw profiles functionality
+
+        label = gtk.Label("You can enable or disable this service for the hardware profiles listed below :")
+        #table.attach(label, 0, 3, 5, 6, 0, 0, 0, 5)
+
+        scrolledwindow = gtk.ScrolledWindow(None, None)
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
+        #table.attach(scrolledwindow, 0, 3, 6, 7, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.profiles_tree_view = gtk.TreeView()
+        scrolledwindow.add(self.profiles_tree_view)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Hardware profile")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.profiles_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 0)
+
+        column = gtk.TreeViewColumn()
+        column.set_title("Status")
+        renderer = gtk.CellRendererText()
+        column.pack_start(renderer, True)
+        self.profiles_tree_view.append_column(column)
+        column.add_attribute(renderer, "text", 1)
+
+        self.profiles_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
+        self.profiles_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+        self.profiles_tree_view.set_model(self.profiles_store)
+
+        hbox = gtk.HBox(2, False)
+#        table.attach(hbox, 0, 1, 7, 8, 0, 0, 0, 0)
+
+        self.enable_button = gtk.Button("Enable")
+        hbox.pack_start(self.enable_button, False, False, 0)
+
+        self.disable_button = gtk.Button("Disable")
+        hbox.pack_start(self.disable_button, False, False, 0)
+
+        notebook.set_tab_label(notebook.get_nth_page(1), gtk.Label("Log On"))
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.cancel_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.apply_button = gtk.Button("Apply", gtk.STOCK_APPLY)
+        self.apply_button.set_flags(gtk.CAN_DEFAULT)
+        self.apply_button.set_sensitive(not self.brand_new) # disabled for new group
+        self.add_action_widget(self.apply_button, gtk.RESPONSE_APPLY)
+
+        self.ok_button = gtk.Button("OK", gtk.STOCK_OK)
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.ok_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.local_account_radio.connect("toggled", self.on_local_account_radio_clicked)
+        #self.profiles_tree_view.get_selection().connect("changed", self.on_profiles_tree_view_selection_changed)
+        #self.enable_button.connect("clicked", self.on_enable_button_click)
+        #self.disable_button.connect("clicked", self.on_disable_button_click)
+
+    def check_for_problems(self):
+        if (self.password_entry.get_text() != self.confirm_password_entry.get_text()) and self.this_account_radio.get_active():
+            return "The password was not correctly confirmed. Please ensure that the password and confirmation match exactly."
+
+        return None
+
+    def update_sensitivity(self):
+        local_account = self.local_account_radio.get_active()
+
+        self.allow_desktop_interaction_check.set_sensitive(local_account)
+        self.account_entry.set_sensitive(not local_account)
+        self.password_entry.set_sensitive(not local_account)
+        self.confirm_password_entry.set_sensitive(not local_account)
+
+#        profile = self.get_selected_profile()
+#        if (profile is None):
+#            self.enable_button.set_sensitive(False)
+#            self.disable_button.set_sensitive(False)
+#        else:
+#            self.enable_button.set_sensitive(not profile[1])
+#            self.disable_button.set_sensitive(profile[1])
+
+    def service_to_values(self):
+        if (self.service is None):
+            raise Exception("service not set")
+
+        self.name_label.set_text(self.service.name)
+        self.display_name_entry.set_text(self.service.display_name)
+        self.description_text_view.get_buffer().set_text(self.service.description)
+        self.exe_path_entry.set_text(self.service.path_to_exe)
+
+        temp_dict = {svcctl.SVCCTL_BOOT_START:0, svcctl.SVCCTL_SYSTEM_START:1, svcctl.SVCCTL_AUTO_START:2, svcctl.SVCCTL_DEMAND_START:3, svcctl.SVCCTL_DISABLED:4}
+
+        self.startup_type_combo.set_active(temp_dict[self.service.start_type])
+        self.start_params_entry.set_text(self.service.start_params)
+
+        if (self.service.account is None):
+            self.local_account_radio.set_active(True)
+            self.allow_desktop_interaction_check.set_active(self.service.allow_desktop_interaction)
+        else:
+            self.this_account_radio.set_active(True)
+            self.account_entry.set_text(self.service.account)
+
+            if (self.service.account_password is not None):
+                self.password_entry.set_text(self.service.account_password)
+                self.confirm_password_entry.set_text(self.service.account_password)
+
+        #self.refresh_profiles_tree_view()
+
+    def values_to_service(self):
+        if (self.service is None):
+            raise Exception("service not set")
+
+        temp_dict = {0:svcctl.SVCCTL_BOOT_START, 1:svcctl.SVCCTL_SYSTEM_START, 2:svcctl.SVCCTL_AUTO_START, 3:svcctl.SVCCTL_DEMAND_START, 4:svcctl.SVCCTL_DISABLED}
+
+        self.service.start_type = temp_dict[self.startup_type_combo.get_active()]
+        self.service.start_params = self.start_params_entry.get_text()
+
+        if (self.local_account_radio.get_active()):
+            self.service.account = None
+            self.service.account_password = None
+            self.service.allow_desktop_interaction = self.allow_desktop_interaction_check.get_active()
+        else:
+            self.service.account = self.account_entry.get_text()
+            self.service.account_password = self.password_entry.get_text()
+
+#        del self.service.hw_profile_list[:]
+#
+#        iter = self.profiles_store.get_iter_first()
+#        while (iter is not None):
+#            name = self.profiles_store.get_value(iter, 0)
+#            enabled = self.profiles_store.get_value(iter, 1)
+#            self.service.hw_profile_list.append([name, [False, True][enabled == "Enabled"]])
+#            iter = self.profiles_store.iter_next(iter)
+
+#    def refresh_profiles_tree_view(self):
+#        (model, paths) = self.profiles_tree_view.get_selection().get_selected_rows()
+#
+#        self.profiles_store.clear()
+#        for profile in self.service.hw_profile_list:
+#            self.profiles_store.append((profile[0], ["Disabled", "Enabled"][profile[1]]))
+#
+#        if (len(paths) > 0):
+#            self.profiles_tree_view.get_selection().select_path(paths[0])
+
+#    def get_selected_profile(self):
+#        (model, iter) = self.profiles_tree_view.get_selection().get_selected()
+#        if (iter is None): # no selection
+#            return None
+#        else:
+#            name = model.get_value(iter, 0)
+#            return [profile for profile in self.service.hw_profile_list if profile[0] == name][0]
+
+    def on_local_account_radio_clicked(self, widget):
+        self.update_sensitivity()
+
+#    def on_enable_button_click(self, widget):
+#        profile = self.get_selected_profile()
+#        if (profile is None): # no selection
+#            return
+#
+#        profile[1] = True
+#        self.refresh_profiles_tree_view()
+#
+#    def on_disable_button_click(self, widget):
+#        profile = self.get_selected_profile()
+#        if (profile is None): # no selection
+#            return
+#
+#        profile[1] = False
+#        self.refresh_profiles_tree_view()
+#
+#    def on_profiles_tree_view_selection_changed(self, widget):
+#        self.update_sensitivity()
+
+
+class ServiceControlDialog(gtk.Dialog):
+
+    def __init__(self, service, control):
+        super(ServiceControlDialog, self).__init__()
+
+        self.service = service
+        self.control = control
+        self.cancel_callback = None
+        self.progress_speed = 0.1
+
+        self.create()
+
+    def create(self):
+        self.set_title("Service Control")
+        self.set_border_width(10)
+        self.set_icon_from_file(os.path.join(sys.path[0], "images", "service.png"))
+        self.set_resizable(False)
+        self.set_size_request(400, 150)
+
+        self.control_label = gtk.Label()
+        self.control_label.set_markup("<b>" + ServiceControlDialog.get_control_string(self) + "</b> " + self.service.display_name + "...")
+        self.control_label.set_padding(10, 10)
+        self.control_label.set_ellipsize(pango.ELLIPSIZE_END)
+        self.vbox.pack_start(self.control_label, False, True, 5)
+
+        self.progress_bar = gtk.ProgressBar()
+        self.progress_bar.set_fraction(0.0)
+        self.vbox.pack_start(self.progress_bar, False, True, 5)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_CENTER)
+
+        self.close_button = gtk.Button("Close", gtk.STOCK_CLOSE)
+        self.close_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.close_button, gtk.RESPONSE_CANCEL)
+
+        self.set_default_response(gtk.RESPONSE_CANCEL)
+
+
+        # signals/events
+
+        self.close_button.connect("clicked", self.on_close_button_clicked)
+
+    def get_control_string(self):
+        if (self.control is None):
+            return "Starting"
+        elif (self.control == svcctl.SVCCTL_CONTROL_STOP):
+            return "Stopping"
+        elif (self.control == svcctl.SVCCTL_CONTROL_PAUSE):
+            return "Pausing"
+        elif (self.control == svcctl.SVCCTL_CONTROL_CONTINUE):
+            return "Resuming"
+        else:
+            return ""
+
+    def set_close_callback(self, close_callback):
+        self.close_callback = close_callback
+
+    def progress(self, to_the_end = False):
+        fraction = self.progress_bar.get_fraction()
+
+        if ((fraction + self.progress_speed >= 1.0) or to_the_end):
+            self.progress_bar.set_fraction(1.0)
+        else:
+            self.progress_bar.set_fraction(fraction + self.progress_speed)
+
+    def set_progress_speed(self, progress_speed):
+        self.progress_speed = progress_speed
+
+    def on_close_button_clicked(self, widget):
+        if (self.close_callback is not None):
+            self.close_callback()
+
+
+class SvcCtlConnectDialog(gtk.Dialog):
+
+    def __init__(self, server, transport_type, username, password):
+        super(SvcCtlConnectDialog, self).__init__()
+
+        self.server_address = server
+        self.transport_type = transport_type
+        self.username = username
+        self.password = password
+
+        self.create()
+
+        self.update_sensitivity()
+
+    def create(self):
+        self.set_title("Connect to a server")
+        self.set_border_width(5)
+        self.set_icon_name(gtk.STOCK_CONNECT)
+        self.set_resizable(False)
+
+        # server frame
+
+        self.vbox.set_spacing(5)
+
+        self.server_frame = gtk.Frame("Server")
+        self.vbox.pack_start(self.server_frame, False, True, 0)
+
+        table = gtk.Table(3, 2)
+        table.set_border_width(5)
+        self.server_frame.add(table)
+
+        label = gtk.Label(" Server address: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 0, 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.server_address_entry = gtk.Entry()
+        self.server_address_entry.set_text(self.server_address)
+        self.server_address_entry.set_activates_default(True)
+        table.attach(self.server_address_entry, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Username: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 1, 2, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.username_entry = gtk.Entry()
+        self.username_entry.set_text(self.username)
+        self.username_entry.set_activates_default(True)
+        table.attach(self.username_entry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+        label = gtk.Label(" Password: ")
+        label.set_alignment(0, 0.5)
+        table.attach(label, 0, 1, 2, 3, gtk.FILL, gtk.FILL | gtk.EXPAND, 0, 0)
+
+        self.password_entry = gtk.Entry()
+        self.password_entry.set_text(self.password)
+        self.password_entry.set_visibility(False)
+        self.password_entry.set_activates_default(True)
+        table.attach(self.password_entry, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+
+
+        # transport frame
+
+        self.transport_frame = gtk.Frame(" Transport type ")
+        self.vbox.pack_start(self.transport_frame, False, True, 0)
+
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.transport_frame.add(vbox)
+
+        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP")
+        self.rpc_smb_tcpip_radio_button.set_active(self.transport_type == 0)
+        vbox.pack_start(self.rpc_smb_tcpip_radio_button)
+
+        self.rpc_tcpip_radio_button = gtk.RadioButton(self.rpc_smb_tcpip_radio_button, "RPC over TCP/IP")
+        self.rpc_tcpip_radio_button.set_active(self.transport_type == 1)
+        vbox.pack_start(self.rpc_tcpip_radio_button)
+
+        self.localhost_radio_button = gtk.RadioButton(self.rpc_tcpip_radio_button, "Localhost")
+        self.localhost_radio_button.set_active(self.transport_type == 2)
+        vbox.pack_start(self.localhost_radio_button)
+
+
+        # dialog buttons
+
+        self.action_area.set_layout(gtk.BUTTONBOX_END)
+
+        self.cancel_button = gtk.Button("Cancel", gtk.STOCK_CANCEL)
+        self.add_action_widget(self.cancel_button, gtk.RESPONSE_CANCEL)
+
+        self.connect_button = gtk.Button("Connect", gtk.STOCK_CONNECT)
+        self.connect_button.set_flags(gtk.CAN_DEFAULT)
+        self.add_action_widget(self.connect_button, gtk.RESPONSE_OK)
+
+        self.set_default_response(gtk.RESPONSE_OK)
+
+
+        # signals/events
+
+        self.rpc_smb_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.rpc_tcpip_radio_button.connect("toggled", self.on_radio_button_toggled)
+        self.localhost_radio_button.connect("toggled", self.on_radio_button_toggled)
+
+    def update_sensitivity(self):
+        server_required = not self.localhost_radio_button.get_active()
+
+        self.server_address_entry.set_sensitive(server_required)
+
+    def get_server_address(self):
+        return self.server_address_entry.get_text().strip()
+
+    def get_transport_type(self):
+        if self.rpc_smb_tcpip_radio_button.get_active():
+            return 0
+        elif self.rpc_tcpip_radio_button.get_active():
+            return 1
+        elif self.localhost_radio_button.get_active():
+            return 2
+        else:
+            return -1
+
+    def get_username(self):
+        return self.username_entry.get_text().strip()
+
+    def get_password(self):
+        return self.password_entry.get_text()
+
+    def on_radio_button_toggled(self, widget):
+        self.update_sensitivity()
+
+
+
diff --git a/build/scripts-2.7/gepdump b/build/scripts-2.7/gepdump
new file mode 100644 (file)
index 0000000..0fe7e2b
--- /dev/null
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+#   Unix SMB/CIFS implementation.
+#   GTK+ Endpoint Mapper frontend
+#
+#   Copyright (C) Jelmer Vernooij 2004-2011
+#
+#   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 3 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+#
+# * Show:
+# *  - RPC statistics
+# *  - Available interfaces
+# *   - Per interface: available endpoints
+# *   - Per interface auth details
+#
+
+import gtk, gobject
+import sambagtk
+from samba.dcerpc import mgmt, epmapper
+
+protocol_names = {
+    epmapper.EPM_PROTOCOL_UUID: "UUID",
+    epmapper.EPM_PROTOCOL_NCACN: "NCACN",
+    epmapper.EPM_PROTOCOL_NCALRPC: "NCALRPC",
+    epmapper.EPM_PROTOCOL_NCADG: "NCADG",
+    epmapper.EPM_PROTOCOL_IP: "IP",
+    epmapper.EPM_PROTOCOL_TCP: "TCP",
+    epmapper.EPM_PROTOCOL_NETBIOS: "NetBIOS",
+    epmapper.EPM_PROTOCOL_SMB: "SMB",
+    epmapper.EPM_PROTOCOL_NAMED_PIPE: "PIPE",
+    epmapper.EPM_PROTOCOL_UNIX_DS: "Unix",
+}
+
+def get_protocol_name(protocol):
+    return protocol_names.get(protocol, "Unknown")
+
+
+class EndpointBrowser(gtk.Window):
+
+    def _on_quit1_activate (self, menuitem):
+        gtk.main_quit()
+
+    def _on_about1_activate(self, menuitem):
+        aboutwin = sambagtk.AboutDialog("gepdump")
+        aboutwin.run()
+        aboutwin.destroy()
+
+    def add_epm_entry(self, annotation, t):
+        bd = t.as_binding_string()
+        self._store_eps.append((0, annotation, 1, str(bd), 2, t))
+
+        for floor in t.floors:
+            if floor.lhs.protocol == epmapper.EPM_PROTOCOL_UUID:
+                data = str(floor.get_lhs_data().uuid)
+            else:
+                data = floor.get_rhs_data()
+
+            self._store_eps.append((0, get_protocol_name(floor.lhs.protocol), 1, data, -1))
+
+    def refresh_eps(self):
+        self._store_eps.clear()
+
+        handle = None
+        num_ents = max_ents = 10
+
+        while num_ents == max_ents:
+            (handle, num_ents, ents) = self._epmapper_pipe.Lookup(
+                inquiry_type=0,
+                object=uuid, interface_id=iface, vers_option=0,
+                entry_handle=handle, max_ents=max_ents)
+            for ent in ents:
+                self.add_epm_entry(ent.annotation, ent.tower.tower)
+
+    def _on_refresh_clicked(self, btn):
+        self.refresh_eps()
+
+    def _on_connect_clicked(self, btn):
+        self._epmapper_pipe = sambagtk.RpcConnectDialog(epmapper.epmapper)
+
+        self._mnu_refresh.set_sensitive(True)
+
+        self.refresh_eps()
+
+        self._mgmt_pipe = mgmt.mgmt(self._epmapper_pipe)
+
+    def _on_eps_select(self, selection, model, path, path_currently_selected, data):
+        # Do an InqStats call
+        statistics = self._mgmt_pipe.inq_stats(max_count=mgmt.MGMT_STATS_ARRAY_MAX_SIZE,
+                                         unknown=0)
+
+        if statistics.count != mgmt.MGMT_STATS_ARRAY_MAX_SIZE:
+            raise Exception("Unexpected array size %d" % statistics.count)
+
+        self._lbl_calls_in.set_text("%6d" % statistics[mgmt.MGMT_STATS_CALLS_IN])
+        self._lbl_calls_out.set_text("%6d" % statistics[mgmt.MGMT_STATS_CALLS_OUT])
+        self._lbl_pkts_in.set_text("%wd" % statistics[mgmt.MGMT_STATS_PKTS_IN])
+        self._lbl_pkts_out.set_text("%6d" % statistics[mgmt.MGMT_STATS_PKTS_OUT])
+
+        self._store_princ_names.clear()
+
+        for i in range(100):
+            princ_name = self._mgmt_pipe.inq_princ_name(authn_proto=i, princ_name_size=100)
+            name = gensec_get_name_by_authtype(i)
+            if name is not None:
+                protocol = "%u (%s)" % (i, name)
+            else:
+                protocol = "%u" % i
+
+            self._store_princ_names.append((0, protocol, 1, princ_name))
+
+        return True
+
+    def __init__(self):
+        super(EndpointBrowser, self).__init__()
+        self._create()
+
+    def _create(self):
+        accel_group = gtk.AccelGroup()
+
+        self.set_title("Gtk+ Endpoint Mapper Viewer")
+
+        vbox1 = gtk.VBox(False, 0)
+        vbox1.show()
+        self.add(vbox1)
+
+        menubar1 = gtk.MenuBar()
+        menubar1.show()
+        vbox1.pack_start(menubar1, False, False, 0)
+
+        menuitem1 = gtk.MenuItem ("_File")
+        menuitem1.show()
+        menubar1.add(menuitem1)
+
+        menuitem1_menu = gtk.Menu()
+        menuitem1.set_submenu (menuitem1_menu)
+
+        mnu_connect = gtk.MenuItem ("_Connect")
+        menuitem1_menu.add(mnu_connect)
+
+        self._mnu_refresh = gtk.MenuItem ("_Refresh")
+        menuitem1_menu.add(self._mnu_refresh)
+        self._mnu_refresh.set_sensitive(False)
+
+        quit1 = gtk.ImageMenuItem(gtk.STOCK_QUIT, accel_group)
+        menuitem1_menu.add(quit1)
+
+        menuitem4 = gtk.MenuItem ("_Help")
+        menubar1.add(menuitem4)
+
+        menuitem4_menu = gtk.Menu()
+        menuitem4.set_submenu (menuitem4_menu)
+
+        about1 = gtk.MenuItem ("_About")
+        menuitem4_menu.add(about1)
+
+        hbox2 = gtk.HBox(False, 0)
+        vbox1.add(hbox2)
+
+        scrolledwindow1 = gtk.ScrolledWindow(None, None)
+        hbox2.pack_start(scrolledwindow1, True, True, 0)
+
+        tree_eps = gtk.TreeView()
+
+        curcol = gtk.TreeViewColumn()
+        curcol.set_title("Name")
+        renderer = gtk.CellRendererText()
+        curcol.pack_start(renderer, True)
+
+        tree_eps.append_column(curcol)
+        curcol.add_attribute(renderer, "text", 0)
+
+        curcol = gtk.TreeViewColumn()
+        curcol.set_title("Binding String")
+        renderer = gtk.CellRendererText()
+        curcol.pack_start(renderer, True)
+        curcol.add_attribute(renderer, "text", 1)
+
+
+        tree_eps.append_column(curcol)
+
+        self._store_eps = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_POINTER)
+        tree_eps.set_model(self._store_eps)
+
+        scrolledwindow1.add(tree_eps)
+
+        tree_eps.get_selection().set_select_function(self._on_eps_select, None, None)
+
+        vbox2 = gtk.VBox(False, 0)
+        hbox2.add(vbox2)
+
+        frame1 = gtk.Frame("Interface")
+        vbox2.add(frame1)
+
+        vbox3 = gtk.VBox(False, 0)
+        frame1.add(vbox3)
+        lbl_iface_uuid = gtk.Label("")
+        vbox3.add(lbl_iface_uuid)
+        lbl_iface_version = gtk.Label("")
+        vbox3.add(lbl_iface_version)
+        lbl_iface_name = gtk.Label("")
+        vbox3.add(lbl_iface_name)
+
+        frame1 = gtk.Frame("Statistics")
+        vbox2.add(frame1)
+
+        table_statistics = gtk.Table(4, 2, True)
+        frame1.add(table_statistics)
+
+        table_statistics.attach(gtk.Label("Calls In: "), 0, 1, 0, 1)
+        lbl_calls_in = gtk.Label("")
+        table_statistics.attach(lbl_calls_in, 1, 2, 0, 1)
+        table_statistics.attach(gtk.Label("Calls Out: "), 0, 1, 1, 2)
+        lbl_calls_out = gtk.Label("")
+        table_statistics.attach(lbl_calls_out, 1, 2, 1, 2)
+        table_statistics.attach(gtk.Label("Packets In: "), 0, 1, 2, 3)
+        lbl_pkts_in = gtk.Label("")
+        table_statistics.attach(lbl_pkts_in, 1, 2, 2, 3)
+        table_statistics.attach(gtk.Label("Packets Out: "), 0, 1, 3, 4)
+        lbl_pkts_out = gtk.Label("")
+        table_statistics.attach(lbl_pkts_out, 1, 2, 3, 4)
+
+        frame1 = gtk.Frame("Authentication")
+        vbox2.add(frame1)
+
+        self._treeview_princ_names = gtk.TreeView()
+
+        curcol = gtk.TreeViewColumn()
+        curcol.set_title("Protocol")
+        renderer = gtk.CellRendererText()
+        curcol.pack_start(renderer, True)
+        self._treeview_princ_names.append_column(curcol)
+        curcol.add_attribute(renderer, "text", 0)
+
+        curcol = gtk.TreeViewColumn()
+        curcol.set_title("Principal Name")
+        renderer = gtk.CellRendererText()
+        curcol.pack_start(renderer, True)
+        self._treeview_princ_names.append_column(curcol)
+        curcol.add_attribute(renderer, "text", 1)
+
+        frame1.add(self._treeview_princ_names)
+
+        self._store_princ_names = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_POINTER)
+        self._treeview_princ_names.set_model(self._store_princ_names)
+
+        statusbar = gtk.Statusbar()
+        vbox1.pack_start (statusbar, False, False, 0)
+
+        quit1.connect("activate", self._on_quit1_activate)
+        about1.connect("activate", self._on_about1_activate)
+        mnu_connect.connect ("activate", self._on_connect_clicked)
+        self._mnu_refresh.connect ("activate", self._on_refresh_clicked)
+
+        self.add_accel_group (accel_group)
+
+
+mainwin = EndpointBrowser()
+mainwin.show_all()
+gtk.main()
diff --git a/build/scripts-2.7/gregedit b/build/scripts-2.7/gregedit
new file mode 100644 (file)
index 0000000..e00e659
--- /dev/null
@@ -0,0 +1,621 @@
+#!/usr/bin/python
+#
+#   Unix SMB/CIFS implementation.
+#   GTK+ registry frontend
+#
+#   Copyright (C) Jelmer Vernooij 2004-2005
+#
+#   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 3 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import gtk
+import sambagtk
+from samba import registry
+
+class FindDialog(gtk.Dialog):
+
+    def _create(self):
+        self.set_title("Find Key or Value")
+        self.set_resizable(False)
+        self.set_type_hint(GDK_WINDOW_TYPE_HINT_DIALOG)
+
+        dialog_vbox2 = GTK_DIALOG (FindDialog).vbox
+
+        vbox1 = gtk_vbox_new (False, 0)
+        gtk_box_pack_start (GTK_BOX (dialog_vbox2), vbox1, True, True, 0)
+
+        hbox1 = gtk.HBox(False, 0)
+        gtk_box_pack_start (GTK_BOX (vbox1), hbox1, True, True, 0)
+
+        label6 = gtk.Label("Find String")
+        gtk_box_pack_start (GTK_BOX (hbox1), label6, False, False, 0)
+
+        entry_pattern = gtk_entry_new ()
+        gtk_box_pack_start (GTK_BOX (hbox1), entry_pattern, True, True, 0)
+
+        frame3 = gtk_frame_new (NULL)
+        gtk_box_pack_start (GTK_BOX (vbox1), frame3, True, True, 0)
+        gtk_frame_set_shadow_type (GTK_FRAME (frame3), GTK_SHADOW_NONE)
+
+        alignment3 = gtk_alignment_new (0.5, 0.5, 1, 1)
+        gtk_container_add (GTK_CONTAINER (frame3), alignment3)
+        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3), 0, 0, 12, 0)
+
+        vbox2 = gtk_vbox_new (False, 0)
+        gtk_container_add (GTK_CONTAINER (alignment3), vbox2)
+
+        checkbutton1 = gtk_check_button_new_with_mnemonic ("_Key Names")
+        gtk_box_pack_start (GTK_BOX (vbox2), checkbutton1, False, False, 0)
+
+        checkbutton2 = gtk_check_button_new_with_mnemonic ("_Value Names")
+        gtk_box_pack_start (GTK_BOX (vbox2), checkbutton2, False, False, 0)
+
+        checkbutton3 = gtk_check_button_new_with_mnemonic ("Value _Data")
+        gtk_box_pack_start (GTK_BOX (vbox2), checkbutton3, False, False, 0)
+
+        label7 = gtk.Label("<b>Search in</b>")
+        gtk_frame_set_label_widget (GTK_FRAME (frame3), label7)
+        gtk_label_set_use_markup (GTK_LABEL (label7), True)
+
+        dialog_action_area2 = GTK_DIALOG (FindDialog).action_area
+        gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area2), GTK_BUTTONBOX_END)
+
+        cancelbutton2 = gtk_button_new_from_stock ("gtk-cancel")
+        gtk_dialog_add_action_widget (GTK_DIALOG (FindDialog), cancelbutton2, GTK_RESPONSE_CANCEL)
+        GTK_WIDGET_SET_FLAGS (cancelbutton2, GTK_CAN_DEFAULT)
+
+        okbutton2 = gtk_button_new_from_stock ("gtk-ok")
+        gtk_dialog_add_action_widget (GTK_DIALOG (FindDialog), okbutton2, GTK_RESPONSE_OK)
+        GTK_WIDGET_SET_FLAGS (okbutton2, GTK_CAN_DEFAULT)
+
+        gtk_widget_show_all (dialog_vbox2)
+
+        return FindDialog
+
+
+class SetValueDialog(gtk.Dialog):
+
+    def __init__(self):
+        super(SetValueDialog, self).__init__()
+        SetValueDialog = gtk_dialog_new ()
+        self.set_title("Set Registry Value")
+        self.set_position(GTK_WIN_POS_CENTER)
+        self.set_resizable(False)
+        self.set_type_hint(GDK_WINDOW_TYPE_HINT_DIALOG)
+
+        table1 = gtk_table_new (3, 2, False)
+        self.vbox.pack_start(table1, True, True, 0)
+
+        label3 = gtk.Label("Value name:")
+        table1.attach (label3, 0, 1, 0, 1,
+                        GTK_FILL,
+                        0, 0, 0)
+        label3.set_alignment (0, 0.5)
+
+        label4 = gtk.Label("Data Type:")
+        table1.attach (label4, 0, 1, 1, 2,
+                        GTK_FILL, 0, 0, 0)
+        label4.set_alignment(0, 0.5)
+
+        label5 = gtk.Label("Data:")
+        table1.attach (label5, 0, 1, 2, 3,
+                        GTK_FILL,
+                        0, 0, 0)
+        label5.set_alignment(0, 0.5)
+
+        entry_name = entry_value_name = gtk.Entry()
+        table1.attach (entry_value_name, 1, 2, 0, 1,
+                        GTK_EXPAND | GTK_FILL,
+                        0, 0, 0)
+
+        entry_data = value_data = gtk_entry_new ()
+        table1.attach (value_data, 1, 2, 2, 3,
+                        GTK_EXPAND | GTK_FILL,
+                        0, 0, 0)
+
+        entry_type = combo_data_type = gtk.ComboBox()
+
+        gtk_combo_box_append_text((combo_data_type), "REG_NONE")
+        gtk_combo_box_append_text((combo_data_type), "REG_SZ")
+        gtk_combo_box_append_text((combo_data_type), "REG_EXPAND_SZ")
+        gtk_combo_box_append_text((combo_data_type), "REG_BINARY")
+        gtk_combo_box_append_text((combo_data_type), "REG_DWORD_LE")
+        gtk_combo_box_append_text((combo_data_type), "REG_DWORD_BE")
+
+        table1.attach (combo_data_type, 1, 2, 1, 2,
+                        GTK_FILL,
+                        GTK_FILL, 0, 0)
+
+        dialog_action_area1 = self.action_area
+        dialog_action_area1.set_layout(GTK_BUTTONBOX_END)
+
+        cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel")
+        gtk_dialog_add_action_widget (GTK_DIALOG (SetValueDialog), cancelbutton1, GTK_RESPONSE_CANCEL)
+        cancelbutton1.set_flags(GTK_CAN_DEFAULT)
+
+        okbutton1 = gtk_button_new_from_stock ("gtk-ok")
+        self.add_action_widget(okbutton1, GTK_RESPONSE_OK)
+        okbutton1.set_flags(GTK_CAN_DEFAULT)
+
+        gtk_widget_show_all(self.vbox)
+
+
+class NewKeyDialog(gtk.Dialog):
+
+    def __init__(self):
+        super(NewKeyDialog, self).__init__()
+        self.set_title("New Registry Key")
+        self.set_position(GTK_WIN_POS_CENTER)
+        self.set_resizable(False)
+        self.set_type_hint(GDK_WINDOW_TYPE_HINT_DIALOG)
+
+        dialog_vbox2 = self.vbox
+
+        hbox1 = gtk.HBox(False, 0)
+        dialog_vbox2.pack_start(hbox1, True, True, 0)
+
+        label6 = gtk.Label("Name:")
+        hbox1.pack_start(label6, False, False, 0)
+
+        entry_key_name = gtk.Entry()
+        hbox1.pack_start (entry_key_name, True, True, 0)
+
+        dialog_action_area2 = self.action_area
+        dialog_action_area2.set_layout(GTK_BUTTONBOX_END)
+
+        name_entry = entry_key_name
+
+        cancelbutton2 = gtk_button_new_from_stock ("gtk-cancel")
+        self.add_action_widget (cancelbutton2, GTK_RESPONSE_CANCEL)
+        cancelbutton2.set_flags(GTK_CAN_DEFAULT)
+
+        okbutton2 = gtk_button_new_from_stock ("gtk-ok")
+        self.add_action_widget (okbutton2, GTK_RESPONSE_OK)
+        okbutton2.set_flags(GTK_CAN_DEFAULT)
+
+        dialog_vbox2.show_all()
+
+
+def expand_key(treeview, parent, arg2):
+    firstiter = store_keys.iter_children(parent)
+
+    # See if this row has ever had a name gtk_tree_store_set()'ed to it.
+    #          If not, read the directory contents 
+    name = store_keys.get(firsiter, 0)
+
+    if name is not None:
+        return
+
+    k = store_keys.get(parent, 1)
+
+    for(i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index( k, i, &subname, NULL, NULL)); i++) {
+        uint32_t count
+
+        reg_open_key(k, subname, &sub)
+
+        # Replace the blank child with the first directory entry
+        # You may be tempted to remove the blank child node and then 
+        # append a new one.  Don't.  If you remove the blank child 
+        # node GTK gets confused and won't expand the parent row. 
+
+        if i == 0:
+            iter = firstiter
+        else:
+            gtk_tree_store_append(store_keys, &iter, parent)
+        gtk_tree_store_set(store_keys, &iter, 
+                        0, subname,
+                        1, sub,
+                        -1)
+        
+        if (W_ERROR_IS_OK(reg_key_get_info(sub, NULL, &count, NULL, NULL, NULL, NULL, NULL)) && count > 0) 
+            gtk_tree_store_append(store_keys, &tmpiter, &iter)
+    }
+
+    if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) { 
+        gtk_show_werror(mainwin, "While enumerating subkeys", error)
+    }
+
+    def load_hive(self, root, name):
+        store_vals.clear()
+
+        # Add the root */
+        store_keys.append(0, name or "", 1, root, -1)
+
+        save.set_sensitive(True)
+        save_as.set_sensitive(True)
+
+    def load_root(self):
+        if registry is None:
+            return
+        store_vals.clear()
+        store_keys.clear()
+
+        for (i = HKEY_CLASSES_ROOT; i <= HKEY_PERFORMANCE_NLSTEXT; i++):
+            if (!W_ERROR_IS_OK(reg_get_predefined_key(registry, i, &root))) { continue; }
+
+            self.load_hive(root, reg_get_predef_name(i))
+
+    def on_open_file_activate (self, menuitem):
+        openfilewin = create_openfilewin(NULL)
+
+        result = openfilewin.run()
+
+        if result == GTK_RESPONSE_ACCEPT:
+            filename = openfilewin.get_filename()
+            error = reg_open_hive(NULL, filename, NULL, NULL, gtk_event_context(), lp_ctx, &hive_root)
+
+            reg_root = reg_import_hive_key(registry, hive_root, -1, NULL)
+
+            mainwin.set_title("Registry Editor - %s" % filename)
+            store_keys.clear()
+            self.load_hive(reg_root, filename)
+
+        openfilewin.destroy()
+
+    def on_open_local_activate(self, menuitem):
+        registry = samba.registry.open_local()
+        self.load_root()
+
+    def on_open_remote_activate(self, menuitem):
+        rpcwin = RpcConnectDialog()
+        result = rpcwin.run()
+
+        if result != GTK_RESPONSE_ACCEPT:
+            rpcwin.destroy()
+            return
+
+        creds = Credentials()
+        creds.guess(lp_ctx)
+        cli_credentials_set_gtk_callbacks(creds)
+
+        registry = samba.registry.open_remote(creds, lp_ctx, 
+                    rpcwin.get_binding_string())
+
+        tmp = "Registry Editor - Remote Registry at %s" % rpcwin.get_host()
+        self.set_title (tmp)
+
+        self.load_root()
+
+        rpcwin.destroy()
+
+    def on_save_as_activate(self, menuitem):
+        error = WERR_OK
+        savefilewin = create_savefilewin(NULL)
+        result = savefilewin.run()
+        if result == GTK_RESPONSE_ACCEPT:
+        # FIXME:        error = reg_dump(registry, gtk_file_selection_get_filename(GTK_FILE_SELECTION(savefilewin))); 
+            if (!W_ERROR_IS_OK(error)) {
+                gtk_show_werror(mainwin, "Error while saving as", error)
+            }
+        savefilewin.destroy()
+
+    def on_quit_activate(self, menuitem):
+        gtk.main_quit()
+
+    def on_delete_value_activate(self, menuitem):
+        if tree_vals.get_selected() is None:
+            return
+
+        gtk_tree_model_get(store_vals, &iter, 0, &value, -1)
+        
+        reg_del_value(current_key, value)
+
+    def on_delete_key_activate(self, menuitem):
+
+        if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_keys)), NULL, &iter)) {
+            return
+        }
+
+        if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(store_keys), &parentiter, &iter)) {
+            return
+        }
+        
+        gtk_tree_model_get(GTK_TREE_MODEL(store_keys), &parentiter, 1, 
+                           &parent_key, -1)
+        
+        # FIXME 
+        # reg_key_del(parent_key, current_key->name); 
+
+    def on_add_key_activate(self, menuitem, user_data):
+        addwin = NewKeyDialog()
+        result = addwin.run()
+
+        if result == GTK_RESPONSE_OK:
+            newkey = reg_key_add_name(current_key, entry.get_text())
+
+        addwin.destroy()
+
+    def on_value_activate(self, treeview, arg1, arg2):
+        addwin = SetValueDialog(&entry_type, &entry_value)
+
+        iter = store_vals.get_iter(arg1)
+
+        valname = store_vals.get(iter, 0)
+        valdesc = store_vals.get(iter, 2)
+        valtype = store_vals.get(iter, 3)
+
+        addwin.entry_name.set_sensitive(False)
+        entry_name.set_text(valname)
+        entry_value.set_text(valdesc)
+        addwin.entry_type.set_active(valtype)
+        
+        result = addwin.run()
+        if result == GTK_RESPONSE_OK):
+            (data_type, data) = reg_string_to_val(str_regtype(entry_type.get_active(), entry_value.get_text())
+            
+            reg_val_set(current_key, entry_name.get_text(), data_type, data)
+        addwin.destroy()
+
+    def on_set_value_activate(self, menuitem, user_data):
+        addwin = SetValueDialog()
+        result = addwin.run()
+        if result == GTK_RESPONSE_OK:
+            (data_type, data) = reg_string_to_val(str_regtype(entry_type.get_active()), entry_value.get_text())
+            
+            reg_val_set(current_key, entry_name.get_text(), data_type, data)
+        addwin.destroy()
+
+    def on_find_activate(self, menuitem, user_data):
+        findwin = FindDialog()
+        # gint result = gtk_dialog_run(findwin)
+        # FIXME 
+        # gtk_widget_destroy(GTK_WIDGET(findwin))
+
+    def on_about_activate(self, menuitem):
+        aboutwin = sambagtk.AboutDialog("gregedit")
+        aboutwin.run()
+        aboutwin.destroy()
+
+    def on_key_activate(self, selection, model, path, path_currently_selected, data):
+        mnu_add_key.set_sensitive(!path_currently_selected)
+        mnu_set_value.set_sensitive(!path_currently_selected)
+        mnu_del_key.set_sensitive(!path_currently_selected)
+        mnu_del_value.set_sensitive(!path_currently_selected)
+        mnu_find.set_sensitive(!path_currently_selected)
+
+        if path_currently_selected:
+            current_key = None
+            return True
+
+        parent = store_keys.get_iter(path)
+        k = store_keys.get(parent, 1, -1)
+
+        current_key = k
+
+        if k is None:
+            return False
+
+        store_vals.clear()
+
+        for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(k, i, &valname, &valtype, &valdata)); i++) {
+            GtkTreeIter iter
+            gtk_list_store_append(store_vals, &iter)
+            gtk_list_store_set (store_vals, &iter, 
+                    0, valname,
+                    1, str_regtype(valtype),
+                    2, reg_val_data_string(iconv_convenience, valtype, valdata),
+                    3, valtype,
+                    -1)
+        }
+
+        if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+             gtk_show_werror(mainwin, "Error while enumerating values",  error)
+             return False
+        }
+        return True
+
+def create_mainwindow():
+    accel_group = gtk.AccelGroup()
+
+    mainwin = gtk_window_new (gtk.WINDOW_TOPLEVEL)
+    mainwin.set_title ("Registry editor")
+    mainwin.set_default_size (642, 562)
+
+    vbox1 = gtk_vbox_new (False, 0)
+    mainwin.add(vbox1)
+
+    menubar = gtk_menu_bar_new ()
+    vbox1.pack_start(menubar, False, False, 0)
+
+    menu_file = gtk_menu_item_new_with_mnemonic ("_File")
+    menubar.add(menu_file)
+
+    menu_file_menu = gtk_menu_new ()
+    menu_file.set_submenu (menu_file_menu)
+
+    open_local = gtk_menu_item_new_with_mnemonic ("Open _Local")
+    menu_file_menu.add( open_local)
+    open_local.connect("activate", self.on_open_local_activate)
+
+    open_remote = gtk_menu_item_new_with_mnemonic ("Open _Remote")
+    menu_file_menu.add(open_remote)
+
+    open_remote.connect("activate", self.on_open_remote_activate)
+
+    separatormenuitem1 = gtk_menu_item_new ()
+    menu_file_menu.add(separatormenuitem1)
+    separatormenuitem1.set_sensitive(False)
+
+    open_file = gtk_image_menu_item_new_with_mnemonic("Open _Hive File")
+    menu_file_menu.add(open_file)
+
+    open_file.connect("activate", self.on_open_file_activate)
+
+    separatormenuitem1 = gtk_menu_item_new ()
+    menu_file_menu.add(separatormenuitem1)
+    separatormenuitem1.set_sensitive(False)
+
+    save = gtk_image_menu_item_new_from_stock ("gtk-save", accel_group)
+    save.set_sensitive(False)
+    menu_file_menu.add(save)
+
+    save_as = gtk_image_menu_item_new_from_stock ("gtk-save-as", accel_group)
+    save_as.set_sensitive(False )
+    menu_file_menu.add(save_as)
+
+    separatormenuitem1 = gtk_menu_item_new ()
+    menu_file_menu.add(separatormenuitem1)
+    separatormenuitem1.set_sensitive(False)
+
+    quit = gtk_image_menu_item_new_from_stock ("gtk-quit", accel_group)
+    menu_file_menu.add(quit)
+
+    men_key = gtk_menu_item_new_with_mnemonic ("_Key")
+    menubar.add(men_key)
+
+    men_key_menu = gtk_menu_new ()
+    men_key.set_submenu(men_key_menu)
+
+    mnu_add_key = gtk_image_menu_item_new_with_mnemonic("Add _Subkey")
+    mnu_add_key.set_image(gtk_image_new_from_stock("gtk-add", GTK_ICON_SIZE_MENU))
+
+    mnu_add_key.set_sensitive(False)
+    men_key_menu.add(mnu_add_key)
+
+    mnu_set_value = gtk_image_menu_item_new_with_mnemonic("Set _Value")
+    mnu_set_value.set_sensitive(False)
+    mnu_set_value.set_image(gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU))
+    men_key_menu.add(mnu_set_value)
+
+    mnu_find = gtk_image_menu_item_new_from_stock ("gtk-find", accel_group)
+    mnu_find.set_sensitive(False)
+    men_key_menu.add(mnu_find)
+
+    mnu_del_key = gtk_image_menu_item_new_with_mnemonic ("Delete Key"); 
+    gtk_widget_set_sensitive(mnu_del_key, False)
+    mnu_del_value.set_image(gtk_image_new_from_stock ("gtk-delete", GTK_ICON_SIZE_MENU))
+    men_key_menu.add(mnu_del_key)
+
+    mnu_del_value = gtk_image_menu_item_new_with_mnemonic ("Delete Value"); 
+    mnu_del_value.set_sensitive(False)
+    mnu_del_value.set_image(gtk_image_new_from_stock ("gtk-delete", GTK_ICON_SIZE_MENU))
+    men_key_menu.add(mnu_del_value)
+
+    help = gtk_menu_item_new_with_mnemonic ("_Help")
+    menubar.add(help)
+
+    help_menu = gtk_menu_new ()
+    help.set_submenu(help_menu)
+
+    about = gtk_menu_item_new_with_mnemonic ("_About")
+    help_menu.add(about)
+
+    hbox1 = gtk.HBox(False, 0)
+    vbox1.pack_start(hbox1, True, True, 0)
+
+    scrolledwindow1 = gtk.ScrolledWindow(NULL, NULL)
+    hbox1.pack_start(scrolledwindow1, True, True, 0)
+
+    tree_keys = gtk.TreeView()
+
+    # Column names
+    curcol = gtk.TreeViewColumn()
+    curcol.set_title("Name")
+    renderer = gtk.CellRendererText()
+    curcol.pack_start(renderer, True)
+
+    tree_keys.append_column(curcol)
+
+    curcol.add_attribute(renderer, "text", 0)
+    scrolledwindow1.add(tree_keys)
+    store_keys = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_POINTER)
+    tree_keys.set_model(store_keys)
+
+    tree_keys.get_selection().set_select_function(on_key_activate)
+
+    tree_keys.connect("row-expanded", self.expand_key)
+
+    scrolledwindow2 = gtk.ScrolledWindow(None, None)
+    hbox1.pack_start(scrolledwindow2, True, True, 0)
+
+    tree_vals = gtk.TreeView()
+    # Column names
+
+    curcol = gtk.TreeViewColumn()
+    curcol.set_title("Name")
+    renderer = gtk.CellRendererText()
+    curcol.pack_start(renderer, True)
+    tree_vals.append_column(curcol)
+    curcol.add_attribute(renderer, "text", 0)
+
+    curcol = gtk.TreeViewColumn()
+    curcol.set_title("Type")
+    renderer = gtk.CellRendererText()
+    curcol.pack_start(renderer, True)
+    tree_vals.append_column(curcol)
+    curcol.add_attribute(renderer, "text", 1)
+
+    curcol = gtk.TreeViewColumn()
+    curcol.set_title("Value")
+    renderer = gtk.CellRendererText()
+    curcol.pack_start(renderer, True)
+    tree_vals.append_column(curcol)
+    curcol.add_attribute(renderer, "text", 2)
+
+    scrolledwindow2.add(tree_vals)
+
+    store_vals = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT)
+    tree_vals.set_model(store_vals)
+
+    statusbar = gtk.Statusbar()
+    vbox1.pack_start(statusbar, False, False, 0)
+    statusbar.set_has_resize_grip(False)
+
+    save_as.connect("activate", self.on_save_as_activate)
+    quit.connect("activate", self.on_quit_activate)
+    mnu_add_key.connect("activate", self.on_add_key_activate)
+    mnu_set_value.connect("activate", self.on_set_value_activate)
+    mnu_find.connect("activate", self.on_find_activate)
+    mnu_del_key.connect("activate", self.on_delete_key_activate)
+    mnu_del_value.connect("activate", self.on_delete_value_activate)
+    about.connect("activate", self.on_about_activate)
+    tree_vals.connect("row-activated", self.on_value_activate)
+
+    self.add_accel_group(accel_group)
+
+    return mainwin
+
+def create_openfilewin (parent):
+    openfilewin = gtk_file_chooser_dialog_new ("Select File", parent, GTK_FILE_CHOOSER_ACTION_OPEN,
+                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                           GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                           NULL)
+    openfilewin.set_border_width(10)
+
+    return openfilewin
+
+
+def create_savefilewin (parent):
+    savefilewin = gtk_file_selection_new ("Select File", parent, GTK_FILE_CHOOSER_ACTION_SAVE,
+                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                           NULL)
+
+    savefilewin.set_border_width(10)
+
+    return savefilewin
+
+    def load_defaults(self):
+        registry = samba.registry.open_local()
+        self.load_root()
+
+lp_ctx = loadparm_init()
+lp_load_default(lp_ctx)
+
+mainwin = RegistryEditor()
+mainwin.show_all()
+mainwin.load_defaults()
+
+gtk.main_loop()
diff --git a/build/scripts-2.7/gtkldb b/build/scripts-2.7/gtkldb
new file mode 100644 (file)
index 0000000..42b17cc
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#   
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+"""Simple GTK+ frontend for LDB."""
+
+import gtk
+import gobject
+import ldb
+import os
+import sys
+
+class LdbURLDialog(gtk.Dialog):
+    """Dialog that prompts for a LDB URL.
+
+    Ideally this should remember LDB urls and list them in a combo box.
+    """
+    def __init__(self, parent=None):
+        """Create a new LdbURLDialog. 
+
+        :param parent: Parent window.
+        """
+        super(LdbURLDialog, self).__init__(parent=parent, 
+                buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)
+                )
+        self.vbox.add(gtk.Label("Enter URL:"))
+        self.url = gtk.Entry()
+        self.vbox.add(self.url)
+        self.show_all()
+
+    def get_url(self):
+        return self.url.get_text()
+
+
+def Ldb(url):
+    """Create a new LDB object.
+    
+    :param url: LDB URL to connect to.
+    """
+    ret = ldb.Ldb()
+    path = os.getenv("LDB_MODULES_PATH")
+    if path is not None:
+        ret.set_modules_dir(path)
+    ret.connect(url)
+    return ret
+
+
+class LdbBrowser(gtk.Window):
+    """GTK+ based LDB browser.
+    """
+    def set_ldb(self, ldb):
+        """Change the LDB object displayed.
+
+        Will refresh the window.
+        
+        :param ldb: New LDB object to use.
+        """
+        self.ldb = ldb
+        self.menu_disconnect.set_sensitive(True)
+        self._fill_tree()
+
+    def _cb_connect(self, button):
+        dialog = LdbURLDialog()
+        if dialog.run() == gtk.RESPONSE_OK:
+            self.set_ldb(Ldb(dialog.get_url()))
+        dialog.destroy()
+
+    def _cb_open(self, button):
+        dialog = gtk.FileChooserDialog(title="Please choose a file", 
+                                       parent=self,
+                                      buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        if dialog.run() == gtk.RESPONSE_OK:
+            self.set_ldb(Ldb(dialog.get_filename()))
+
+        dialog.destroy()
+
+    def _cb_disconnect(self, button):
+        self.treemodel.clear()
+        self.attributemodel.clear()
+        self.menu_disconnect.set_sensitive(False)
+        self.ldb = None
+
+    def _fill_tree(self, hide_special=False):
+        self.treemodel.clear()
+        paths = {}
+        def add_node(dn):
+            if dn.is_special() and hide_special:
+                return None
+            if paths.has_key(str(dn)):
+                return paths[str(dn)]
+            parent_dn = dn.parent()
+            text = str(dn)
+            if parent_dn is not None and str(parent_dn) != '':
+                parent = add_node(parent_dn)
+                text = text[:-len(str(parent_dn))].rstrip(",")
+            else:
+                parent = None
+            paths[str(dn)] = self.treemodel.append(parent, [text, dn])
+
+        for msg in self.ldb.search(None, ldb.SCOPE_SUBTREE, None, ["dn"]):
+            add_node(msg.dn)
+
+    def _toggle_special_entries(self, item):
+        self._fill_tree(item.get_active())
+
+    def _treeview_cursor_cb(self, item):
+        (model, iter) = item.get_selection().get_selected()
+        dn = model.get_value(iter, 1)
+        self.attributemodel.clear()
+        msg = self.ldb.search(dn, ldb.SCOPE_BASE)[0]
+        for name, el in msg.iteritems():
+            if name == "dn":
+                continue
+            for val in set(el):
+                self.attributemodel.append([name, val, el])
+
+    def __init__(self):
+        super(LdbBrowser, self).__init__()
+        vbox = gtk.VBox(spacing=0)
+        
+        # Menu
+        self.menu = gtk.MenuBar()
+        menuitem_db = gtk.MenuItem("_Database")
+        menu_db = gtk.Menu()
+        menuitem_db.set_submenu(menu_db)
+        self.menu.add(menuitem_db)
+
+        # Database menu
+        menu_connect = gtk.MenuItem("Connect to _URL...")
+        menu_connect.connect('activate', self._cb_connect)
+        menu_db.add(menu_connect)
+
+        menu_open = gtk.MenuItem("Open _File...")
+        menu_open.connect('activate', self._cb_open)
+        menu_db.add(menu_open)
+
+        self.menu_disconnect = gtk.MenuItem("_Disconnect")
+        self.menu_disconnect.connect('activate', self._cb_disconnect)
+        self.menu_disconnect.set_sensitive(False)
+        menu_db.add(self.menu_disconnect)
+
+        menu_db.add(gtk.SeparatorMenuItem())
+        menu_hide_special = gtk.CheckMenuItem("_Hide special entries")
+        menu_hide_special.connect('toggled', self._toggle_special_entries)
+        menu_db.add(menu_hide_special)
+
+        menu_db.add(gtk.SeparatorMenuItem())
+        menu_exit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+        menu_exit.connect('activate', lambda x: gtk.main_quit())
+
+        menu_db.add(menu_exit)
+
+        vbox.pack_start(self.menu, expand=False)
+
+        self.treeview = gtk.TreeView()
+        self.treemodel = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self.treeview.set_model(self.treemodel)
+        self.treeview.set_headers_visible(False)
+        self.treeview.append_column(gtk.TreeViewColumn("_Dn", 
+                                    gtk.CellRendererText(), text=0))
+        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
+        self.attributeview = gtk.TreeView()
+        self.attributemodel = gtk.ListStore(str, str, gobject.TYPE_PYOBJECT)
+        self.attributeview.set_model(self.attributemodel)
+        self.attributeview.append_column(gtk.TreeViewColumn("_Name", 
+                                         gtk.CellRendererText(), text=0))
+        self.attributeview.append_column(gtk.TreeViewColumn("_Value", 
+                                         gtk.CellRendererText(), text=1))
+        pane = gtk.HPaned()
+        pane.set_position(200)
+        treeview_window = gtk.ScrolledWindow()
+        treeview_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        treeview_window.add(self.treeview)
+        pane.pack1(treeview_window, resize=False, shrink=True)
+        attributeview_window = gtk.ScrolledWindow()
+        attributeview_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        attributeview_window.add(self.attributeview)
+        pane.pack2(attributeview_window, shrink=True)
+        vbox.pack_start(pane, fill=True, expand=True)
+
+        self.statusbar = gtk.Statusbar()
+        vbox.pack_end(self.statusbar, expand=False)
+
+        self.add(vbox)
+        self.set_default_size(700, 500)
+        self.show_all()
+
+
+browser = LdbBrowser()
+if len(sys.argv) > 1:
+    browser.set_ldb(Ldb(sys.argv[1]))
+gtk.main()
diff --git a/merge_test b/merge_test
new file mode 100644 (file)
index 0000000..fabb0e7
--- /dev/null
@@ -0,0 +1 @@
+ meger test
index 417ebf48fd960b964cc84c9d1edecb56ff6fc7e0..3b6836fc3a3ad8b45c05e7b6dcba22521f116acd 100644 (file)
@@ -33,7 +33,7 @@ class AboutDialog(gtk.AboutDialog):
         self.set_version(samba.version)
         self.set_logo(icon)
         self.set_copyright("Copyright \xc2\xa9 2010 Sergio Martins <Sergio97@gmail.com>")
-        self.set_authors(["Sergio Martins <Sergio97@gmail.com>", "Calin Crisan <ccrisan@gmail.com>", "Jelmer Vernooij <jelmer@samba.org>"])
+        self.set_authors(["Sergio Martins <Sergio97@gmail.com>", "Calin Crisan <ccrisan@gmail.com>", "Dhananjay Sathe <dhananajaysathe@gmail.com>","Jelmer Vernooij <jelmer@samba.org>"])
         self.set_comments(description)
         self.set_wrap_license(True)
         self.set_license("""
diff --git a/sambagtk/images/samba-logo-small.png b/sambagtk/images/samba-logo-small.png
new file mode 100644 (file)
index 0000000..475cc26
Binary files /dev/null and b/sambagtk/images/samba-logo-small.png differ
diff --git a/sambagtk/main.glade b/sambagtk/main.glade
new file mode 100644 (file)
index 0000000..8a11a69
--- /dev/null
@@ -0,0 +1,509 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkWindow" id="main_window">
+    <property name="window_position">center</property>
+    <property name="default_width">800</property>
+    <property name="default_height">600</property>
+    <signal name="destroy" handler="on_main_window_destroy"/>
+    <signal name="key_press_event" handler="on_main_window_key_press_event"/>
+    <child>
+      <object class="GtkVBox" id="vbox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkViewport" id="menubar_viewport">
+            <property name="visible">True</property>
+            <property name="resize_mode">queue</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkMenuBar" id="menubar">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkMenuItem" id="menuitem">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_File</property>
+                    <property name="use_underline">True</property>
+                    <child type="submenu">
+                      <object class="GtkMenu" id="menu_file">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImageMenuItem" id="connect_all_item">
+                            <property name="label">gtk-connect</property>
+                            <property name="visible">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="use_stock">True</property>
+                            <signal name="activate" handler="on_connect_all_item_activate"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkImageMenuItem" id="disconnect_all_item">
+                            <property name="label">gtk-disconnect</property>
+                            <property name="visible">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="use_stock">True</property>
+                            <signal name="activate" handler="on_disconnect_all_item_activate"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                            <property name="visible">True</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkImageMenuItem" id="quit_item">
+                            <property name="label">gtk-quit</property>
+                            <property name="visible">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="use_stock">True</property>
+                            <signal name="activate" handler="on_quit_item_activate"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkMenuItem" id="menuitem2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Edit</property>
+                    <property name="use_underline">True</property>
+                    <child type="submenu">
+                      <object class="GtkMenu" id="menu_edit">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImageMenuItem" id="clear_log_item">
+                            <property name="label">Clear Log</property>
+                            <property name="visible">True</property>
+                            <property name="image">image2</property>
+                            <property name="use_stock">False</property>
+                            <signal name="activate" handler="on_clear_log_activate"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkMenuItem" id="menuitem3">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_View</property>
+                    <property name="use_underline">True</property>
+                    <child type="submenu">
+                      <object class="GtkMenu" id="menu_view">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImageMenuItem" id="connection_info_item">
+                            <property name="label">Connection Details</property>
+                            <property name="visible">True</property>
+                            <property name="image">image1</property>
+                            <property name="use_stock">False</property>
+                            <signal name="activate" handler="on_connection_info_item_activate"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkMenuItem" id="menuitem4">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Help</property>
+                    <property name="use_underline">True</property>
+                    <child type="submenu">
+                      <object class="GtkMenu" id="menu_help">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImageMenuItem" id="about_item">
+                            <property name="label">gtk-about</property>
+                            <property name="visible">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="use_stock">True</property>
+                            <signal name="activate" handler="on_about_item_activate"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkViewport" id="toolbar_viewport">
+            <property name="visible">True</property>
+            <property name="resize_mode">queue</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkToolbar" id="toolbar">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkToolButton" id="connect_all_button">
+                    <property name="visible">True</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Connect All</property>
+                    <property name="use_underline">True</property>
+                    <property name="stock_id">gtk-connect</property>
+                    <signal name="clicked" handler="on_connect_all_button_clicked"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="disconnect_all_button">
+                    <property name="visible">True</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Disconnect All</property>
+                    <property name="use_underline">True</property>
+                    <property name="stock_id">gtk-disconnect</property>
+                    <signal name="clicked" handler="on_disconnect_all_button_clicked"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSeparatorToolItem" id="toolbutton3">
+                    <property name="visible">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="clear_log_button">
+                    <property name="visible">True</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Clear Log</property>
+                    <property name="use_underline">True</property>
+                    <property name="stock_id">gtk-clear</property>
+                    <signal name="clicked" handler="on_clear_log_button_clicked"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkNotebook" id="utility_notebook">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="tab_pos">left</property>
+            <property name="homogeneous">True</property>
+            <signal name="switch_page" handler="on_utility_notebook_switch_page"/>
+            <child>
+              <object class="GtkViewport" id="launcher_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox3">
+                        <property name="visible">True</property>
+                        <property name="border_width">3</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkHBox" id="hbox1">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0</property>
+                                <property name="xpad">5</property>
+                                <property name="label" translatable="yes">Server:   </property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="server_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0</property>
+                                <property name="label" translatable="yes">Unknown</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox3">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label2">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0</property>
+                                <property name="xpad">5</property>
+                                <property name="label" translatable="yes">Username:    </property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="username_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0</property>
+                                <property name="label" translatable="yes">Unknown</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox2">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label3">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0</property>
+                                <property name="xpad">5</property>
+                                <property name="label" translatable="yes">Status:    </property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="status_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Unknown</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="padding">5</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTextView" id="messages_textview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="pixels_above_lines">2</property>
+                            <property name="editable">False</property>
+                            <property name="wrap_mode">word</property>
+                            <property name="left_margin">5</property>
+                            <property name="right_margin">5</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="lblServerOptions">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Server Options</property>
+              </object>
+              <packing>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkViewport" id="sam_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="lblUserManager">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">User Manager</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkViewport" id="regedit_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="lblRegEdit">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Registry Editor</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkViewport" id="svcctl_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="lblServicesManager">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Services Manager</property>
+              </object>
+              <packing>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkViewport" id="crontab_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="lblTaskScheduler">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Task Scheduler</property>
+              </object>
+              <packing>
+                <property name="position">4</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="statusbar_hbox">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkProgressBar" id="progressbar">
+                <property name="fraction">0.5</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkStatusbar" id="statusbar">
+                <property name="visible">True</property>
+                <property name="spacing">2</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="stock">gtk-missing-image</property>
+  </object>
+  <object class="GtkImage" id="image2">
+    <property name="visible">True</property>
+    <property name="stock">gtk-clear</property>
+  </object>
+</interface>
index 137d987b07e6eb1d61eca9f47b966109d0c5b11a..d57c083c40bf43c90b05f86ecdb4bf3eadd022d9 100755 (executable)
@@ -752,7 +752,7 @@ class SAMWindow(gtk.Window):
                 return user_list[0]
             else:
                 return None
-
+    #test
     def get_selected_group(self):
         if not self.connected():
             return None
index 84b986ade32e7593a3cb566f58f3623e3c186686..18449975d275d9a38425047d32a8b601660d42a2 100644 (file)
@@ -584,7 +584,7 @@ class SAMConnectDialog(gtk.Dialog):
         vbox.set_border_width(5)
         self.transport_frame.add(vbox)
 
-        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP")
+        self.rpc_smb_tcpip_radio_button = gtk.RadioButton(None, "RPC over SMB over TCP/IP ")
         self.rpc_smb_tcpip_radio_button.set_active(self.transport_type == 0)
         vbox.pack_start(self.rpc_smb_tcpip_radio_button)
 
@@ -685,4 +685,3 @@ class SAMConnectDialog(gtk.Dialog):
         self.update_sensitivity()
 
 
-