myserver-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myserver-commit] [SCM] GNU MyServer branch, master, updated. c11b70ca46


From: Giuseppe Scrivano
Subject: [myserver-commit] [SCM] GNU MyServer branch, master, updated. c11b70ca463f0b98103b0105c4afd63015d61274
Date: Sun, 30 Aug 2009 10:06:24 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU MyServer".

The branch, master has been updated
       via  c11b70ca463f0b98103b0105c4afd63015d61274 (commit)
       via  7169446b9dc646bc84c9136f9ae68aac83aea0da (commit)
       via  9775d91628a8b8e60249ed5264c0e070aeada169 (commit)
       via  a320fda42e9707b7b5d4d87c5d81f63c36c778ee (commit)
       via  bc75f6f12f214b9d528ff6011d59b20b84dbdbc4 (commit)
       via  ebe91b6cdd15d5f51c086aebb1dae4a2817b8ec9 (commit)
       via  1fe17cec41609d5dcc35e494f6b9e9d6cc190161 (commit)
       via  4d884698e00d9fdd70b6d09bd52c6662ccc78153 (commit)
       via  1e783e320be4a0858adc2ae11b354a2736338586 (commit)
       via  d93d10e52bffcc144bab2a80b57c5c4333912d50 (commit)
       via  1020bab81ba10ebfd055d26e47276874826b4486 (commit)
       via  b4f58841cb0d02634db8f3b85dc86583e6ff44e4 (commit)
       via  a809ac0d448bf74cc4e454fed0d17ce7707c41fb (commit)
       via  4d8bcd32cc36b1ffc31120e0208b96081b52795f (commit)
       via  f4ef782aa3024edf332fd0ec8f25dcfc4a5c127f (commit)
       via  6f59a090d44198c0190a6106641bbff7c7cde845 (commit)
       via  fc81d1bb032e279a2e7436fe95780728f26ee738 (commit)
       via  6b4599d38b577a26c64e31e7630c63c066f6f9f8 (commit)
       via  f3636a24d3828ae7c721380b68acb37ff44d758b (commit)
       via  26f614483d0877a0968819ec45a0e68505ea3a85 (commit)
       via  e096fc5a40603a7c8ce97bb996430fcc06e80534 (commit)
       via  26eb5c3ecba6ede2197b9fe75b6992d467c5d4b9 (commit)
       via  ce56783eff4746a0bd3aee5f3e00d3aa56390652 (commit)
       via  89cb5ac1df6b9b6fc4a13db57a7c1c21554aae6e (commit)
       via  7d0405ec49eb938d208dc72730387ce6afb9e7ba (commit)
       via  07c79e454295c046b8e788ea711e4270040a2a7f (commit)
       via  498effc0fb50a9d74f390b2e138d329f41e70767 (commit)
       via  8f8e920949fafb1b328f69a8630f963ab04a4dca (commit)
       via  78d989514db5fe89b11560a684f57d767dbac17c (commit)
       via  ed000bb9ed508947f31e6f3ad16904430bcfe274 (commit)
       via  cfbe88ed7f1ff39ab79894a272e5164ea2f1a3f6 (commit)
       via  9f48d3563c8cb5fa994177aef1e66358f73e95b8 (commit)
       via  1f9cb30b943f1d1aa820398b4b46c0478c20863c (commit)
       via  e7ad47b23cb6a3c74ac0ae0e5cdc609598ded717 (commit)
       via  9c64c9d44ce1b960917d4bd307935db995ea14d0 (commit)
       via  91f91d95e12396a71f64c29af06172741bd6aa72 (commit)
       via  c91a8e42dbbdd0bd4ab595f9e3b94c0829d0a93c (commit)
       via  65b8080af3f230a44eaea44557de323ff52a1a77 (commit)
       via  7d48adfa5075a1ca7b36ce866fb06fbff6a60152 (commit)
       via  36415041ce2c79cde231ad3008563427ad32f8b1 (commit)
       via  2796f1923fdc16bf6be4da8d4dcc7539104bf7d9 (commit)
       via  36ebea61cf9228a29bffb1c444ab82421babcd35 (commit)
       via  4aa358f6f612d9f9edb05422bb069e0580bd030a (commit)
       via  a02ec8f3d2954ec1b4bac304447a07ff15c4497d (commit)
       via  4171c905b2321945b3ecf71ae65542a7db3c6a2c (commit)
       via  716ad749f0f326562db1a80e08e3cc8b6faf2e21 (commit)
       via  f59a03ce25be48ee894e1db2947bdfd03ac93d71 (commit)
       via  80d6cb01d33e598f0ec1fa7f45cd195794a5c112 (commit)
       via  8f2065d32f68cbb7d6b5584739fadba1e23aefee (commit)
       via  fd58fa5679660d48b52cf4ee0d31e963dabc1b40 (commit)
       via  1cc305c0cd2977b99bcd6dcbad4c97f9b51bb7d9 (commit)
       via  f9d4639ef55171aadf5fecfd236c5905b6e4c007 (commit)
       via  7c1da124afaac94910e59244fa1b01f0504a7e5c (commit)
       via  3ff6dcf1f152b1961df5f9f4da1c4213428b194a (commit)
       via  5de9a2c8d6f8121831dc8752a14fcc932bcb43e5 (commit)
       via  e88e3a334dfb8c841dfbd9f7cb21511fac6c18f5 (commit)
       via  d083c2b87c819b698923635febfcd83e841fe402 (commit)
       via  f9ecb02a7fe208e42c7b26cecd809dffd15e60ac (commit)
       via  5eb9ceee5cb46677d0ed82d0b99ca40deec9fa27 (commit)
       via  4db2041c0917463789b51cd594ee24d6ced2ad35 (commit)
       via  8511f7bf10f2a7181186d4b8a63cd9c826378e85 (commit)
       via  9b1a0c54d95307660a0feafb0d2354a0d2c51f4a (commit)
       via  abdcc4fc662dd4923cf11733e6401bf142b348b4 (commit)
       via  b816fd85420cd9f5ba87c0a5508f3c2b9c22edbd (commit)
       via  816ba0b0fd7a695ba9d91ded059b771aa3868d48 (commit)
       via  814c6f4aa71fcfc27ffa488e42999d90b8940cde (commit)
       via  15879dec8ca6c87ac9d8e60b4b8ccb87b2fd53d4 (commit)
       via  44d0809025b967fbed0b62113b02b069921ed4d5 (commit)
       via  dc0f5b23bd5df3d4fbd81bc797042053c97eeb25 (commit)
       via  185d91663d56e922f1c789bb8929cb6021bbe264 (commit)
       via  33d2bdd4bb69fb4584643a2af593089461b7993a (commit)
       via  b5bafeea7a86257a1cce9938b0279c9334f8128d (commit)
       via  c155a354bc98d44543eb748b8e9d16d44540b99d (commit)
       via  e95ef51c3bcc931fd157807e050e4376b6fa2660 (commit)
       via  c3d7e72c5d7a043b32571d78b9dd368f9680722d (commit)
       via  290fa08dae6d19ce02df7a7a13823d5ff319b615 (commit)
       via  0162ed2c63fa02058fdcda88c6b15b124b6744a3 (commit)
       via  fec75c5b9759f29b5a467ee8b32655f864bc82bf (commit)
       via  121d46c683a2a0abf3b6d51a50082aeaf6f1f006 (commit)
       via  179a48e1d95df03a971357c1382463afe26c3960 (commit)
       via  a12bca3717a6645005f19289d03431ed5d2c9cf0 (commit)
       via  d0d888d3379c57f14ee36216d6ba5c4c9a74c075 (commit)
       via  fabadfef11cb000eb1c9d42a21348fb89de40e24 (commit)
       via  a11d241ee8b86aa95f309bcd97f3b17450f9028c (commit)
       via  1d19d2a6c91aafc7c351524f55138ebe760bc30c (commit)
       via  790c62ddd0e69e123cf9e9096e1e3da239c2ad51 (commit)
       via  c993a0924b9857894396d3c5a08340bba10f7725 (commit)
       via  be87aa94c3a48fe25109f269ecc8dfaf82b68397 (commit)
       via  d79ec32935886b1b4864ae217975e81e12268d01 (commit)
       via  d603a00a9f1529278b7cd92754fd06bb8603c98f (commit)
       via  c7bfa02a092d4d79e413a550772cbb5eea4c891a (commit)
       via  fbb478a19413cc0db6222da7bd5f19c75916785b (commit)
       via  e89932b722ca91317d7bbb63bae5c2204132b4c8 (commit)
       via  7ed7acae01c06591c9db43849ced487053beca7e (commit)
       via  c93d32970d1d2947f73853ff21cc42e5921dcc96 (commit)
       via  1a50bf043a18938786ce237a88284b252092ad4d (commit)
       via  420a86f9dd7181b5f8f42a8007cb9ff508721b11 (commit)
       via  e992b4a3e844e463df1189038aa579e337875fe7 (commit)
       via  da3b029e8c7d55ac4c38436303071bb53b04c0f7 (commit)
       via  c723954a698f9c8384a91453727e32fc6c81009f (commit)
       via  c592b21b60297ea739ed2a760bb6933f4671aeec (commit)
       via  c3c98305bd84fd381b95f165e9a647542a966ae2 (commit)
       via  7794e0a8c9ea18c4792754f8890c8f8e4654c341 (commit)
       via  e2f770d7d8fde08d627b40151656928fecdc2c39 (commit)
       via  0010615eede49df77e800edc331c35603434bf26 (commit)
       via  12e27e127621f4b010d27bfdcc15f5bbb307f125 (commit)
       via  a4b940249c6c161fc18274ddef85ef34785f0a6e (commit)
       via  212e40c400279374b006dc3f321e71ab4e41cc13 (commit)
       via  04266076faf692115a2dbf46a8426530216b5f46 (commit)
       via  46ad12574dae76f375e1fb29b1770176eb2036a4 (commit)
       via  d3324c64a1aeac6cd067bc19437b7d7735dd44d2 (commit)
       via  cbfd8d9703f7930d71aa05b01e5415187164cad8 (commit)
       via  0831902383662bdb633b4a3dbb53559dc9428dfc (commit)
       via  22a84c341826c11dbb6a89c6dbb113fbd855c264 (commit)
       via  d77a6a2d6d3e12e0f4bc2cb107de0071dd06daf6 (commit)
       via  ed8d39eb36f7fe6a8800db2f7ddb26a389b5b693 (commit)
       via  d12a997bfa7d627901ee9de2cf9095d9cd61f6d4 (commit)
       via  9800f5ab48f188c8059d31655aa07d93af1c205b (commit)
       via  f8cb67b5f730bb0e7e4dde7e32ef62a4766ee25e (commit)
       via  104c01ca3fa9b00bb9c1c7904025e55475c32789 (commit)
       via  f07fbf3093aa9ac5617313795b7d4aae68ac73ce (commit)
       via  4f880beec5e188d1386d04ab974f954ba50fd862 (commit)
       via  5a1090eeb67304fea24bdf4d11edcec371e595b1 (commit)
       via  daf0d6ba253e47b2dd150519c1eb561672d38c0f (commit)
       via  5849c91c06ae47de0dac22178291e2e4e4eb4f2f (commit)
       via  7e19a0eb34f000df5d91c9ced6a955bca3a0f0df (commit)
       via  b8939650ccef039a9e38e4003a3ceadd9ee7ffb6 (commit)
       via  2376d719fa8b2ae79adb58f8f36ba2791f010ff1 (commit)
       via  d428818d42962674193ea30b2992419d1e9aef3a (commit)
       via  16aa7785a4d22f4b317e46408a3fff213f5ebe70 (commit)
       via  61ac4a10693bea6f209bfda238d0a4787bbc6f0a (commit)
       via  afe67f2f23bd7750d32095e70e14bb8708068af0 (commit)
       via  2cfed9a1f282424766351086df9ee5c6cdb9ff35 (commit)
       via  c66b92cc63c15a55abae354e818bcab4086f2d61 (commit)
       via  101f031c7d413535def126b9f59a8f918e8d8468 (commit)
       via  8cb4b723a21d59214f73b9b5b82efbb7efae711d (commit)
       via  2e4765307dd1a8ac4f63d89af67a9544bc45a8dc (commit)
       via  fbdd0f4735ccddda0530adc0ab706ff1d8e8b12e (commit)
       via  5064583a62c20962fd089ff747b8d0d9538a574b (commit)
       via  c32449cf08ef4588c38a8fbd679db03c0c76be4c (commit)
       via  4a5ec3753456e100c888234c97712fcd4f039237 (commit)
       via  58e336c9d612a1db3073b80d079652eae6dd1cbc (commit)
       via  fae84295c23e72dca20766fba304f030f882365f (commit)
       via  6ad378bcc4489348696a846ae8f17b5c8f8455eb (commit)
       via  6c4d700d2fc3a4e9174ebae276cd9e3bb5a4da2f (commit)
       via  2e82879117b0d54b6c9a61519a3511e6e4be4136 (commit)
       via  055e94570934d1e39f40552d0af4c762f57dbdeb (commit)
       via  2123234285a7d3c72911f42f5b270e4a91bc149e (commit)
      from  2c8092b09071aa763391109b87d2c20f96b5aec6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------


commit c11b70ca463f0b98103b0105c4afd63015d61274
Merge: 2c8092b 7169446
Author: Giuseppe Scrivano <address@hidden>
Date:   Sun Aug 30 12:00:42 2009 +0200

    Merge branch 'master' of 
http://students.mimuw.edu.pl/~ms262969/GNU/MyServer/myserver




commit 7169446b9dc646bc84c9136f9ae68aac83aea0da
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 26 22:33:09 2009 +0200

    Added drag and drop in security files editor.
    
    GTK+ 2.16.5 sometimes segfaults while doing drag and drop. It's probably
    unrelated to this code though.

diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index 23c3be4..9957fbe 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -19,7 +19,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 import gtk
 import gobject
 from MyServer.pycontrollib.security import SecurityList, User, Return, \
-    Condition, Permission
+    Condition, Permission, SecurityElement
 from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 
 class SecurityTree(gtk.TreeView):
@@ -37,6 +37,48 @@ class SecurityTree(gtk.TreeView):
 
         self.last_selected = None
         self.connect('cursor-changed', self.cursor_changed)
+        
+        self.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
+                                      [('text/plain', 0, 0)],
+                                      gtk.gdk.ACTION_MOVE)
+        self.enable_model_drag_dest([('text/plain', gtk.TARGET_SAME_WIDGET, 
0)],
+                                    gtk.gdk.ACTION_MOVE)
+        def get_data(tree, context, selection, target_id, etime):
+            model, selected = tree.get_selection().get_selected()
+            data = model.get_value(selected, 1)
+            selection.set(selection.target, 8, str(data))
+        self.connect('drag_data_get', get_data)
+        def data_received(tree, context, x, y, selection, info, etime):
+            model = tree.get_model()
+            try:
+                data = SecurityElement.from_string(selection.data)
+            except:
+                return # will fall if someone moves SECURITY
+            drop_info = tree.get_dest_row_at_pos(x, y)
+            if drop_info:
+                path, position = drop_info
+                it = model.get_iter(path)
+                if position in [gtk.TREE_VIEW_DROP_BEFORE,
+                                gtk.TREE_VIEW_DROP_AFTER]:
+                    parent_tag = model.get_value(model.iter_parent(it), 0)
+                else:
+                    parent_tag = model.get_value(it, 0)
+                # limit places where rows can be dropped
+                if not ((data.tag == 'DEFINE element' and
+                         parent_tag == 'DEFINE tree') or
+                        parent_tag in ['SECURITY', 'CONDITION']):
+                    return
+                if position == gtk.TREE_VIEW_DROP_BEFORE:
+                    model.insert_before(None, it, (data.tag, data, ))
+                elif position == gtk.TREE_VIEW_DROP_AFTER:
+                    model.insert_after(None, it, (data.tag, data, ))
+                else:
+                    model.append(it, (data.tag, data, ))
+            else:
+                return # drop on widget but not on any row
+            if context.action == gtk.gdk.ACTION_MOVE:
+                context.finish(True, True, etime)
+        self.connect('drag_data_received', data_received)
 
         self.scroll = gtk.ScrolledWindow()
         self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)



commit 9775d91628a8b8e60249ed5264c0e070aeada169
Merge: a320fda 8879eac
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Aug 24 01:46:10 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit a320fda42e9707b7b5d4d87c5d81f63c36c778ee
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Aug 23 20:47:11 2009 +0200

    Don't throw unhandled exceptions when security.xml can't be read.

diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index 451a4bd..23c3be4 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -651,7 +651,7 @@ class SecurityTable(gtk.Table):
             security = SecurityList.from_string(
                 browser.get_file('security.xml'))
             self.security_tree.set_up(security)
-        except IOError:
+        except:
             self.security_tree.clear()
 
     def write_to_file(self, browser):



commit bc75f6f12f214b9d528ff6011d59b20b84dbdbc4
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Aug 22 23:44:16 2009 +0200

    Security file elements can be removed from GUI.

diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index b5f6649..451a4bd 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -288,6 +288,12 @@ class SecurityTree(gtk.TreeView):
             add = DefinitionTree()
         model.append(selected, (tag, add, ))
 
+    def remove_element(self):
+        model, selected = self.get_selection().get_selected()
+        model.remove(selected)
+        self.last_selected = None
+        self.security_table.switch_table('SECURITY')
+
     def export(self):
         def add_definition_tree(parent, i, model):
             while len(parent.get_definitions()): # remove all children
@@ -332,87 +338,99 @@ class SecurityTree(gtk.TreeView):
         return security
 
 class UserTable(gtk.Table):
-    def __init__(self):
-        gtk.Table.__init__(self, 7, 3)
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 8, 3)
+
+        def remove_element(button, tree):
+            tree.remove_element()
+        button = gtk.Button('Remove this element')
+        button.connect('clicked', remove_element, security_tree)
+        self.attach(button, 0, 2, 0, 1, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('name'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('name'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.name_entry = gtk.Entry()
-        self.attach(self.name_entry, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.attach(self.name_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.name_check = gtk.CheckButton()
-        self.attach(self.name_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(self.name_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
-        self.attach(gtk.Label('password'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('password'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
         self.password_entry = gtk.Entry()
-        self.attach(self.password_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(self.password_entry, 1, 2, 2, 3, yoptions = gtk.FILL)
         self.password_check = gtk.CheckButton()
-        self.attach(self.password_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(self.password_check, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
 
         def add_options(combo):
             combo.append_text('Yes')
             combo.append_text('No')
             combo.append_text('empty')
 
-        self.attach(gtk.Label('READ'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('READ'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
         self.read_combo = gtk.combo_box_new_text()
         add_options(self.read_combo)
-        self.attach(self.read_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+        self.attach(self.read_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('EXECUTE'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('EXECUTE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
         self.execute_combo = gtk.combo_box_new_text()
         add_options(self.execute_combo)
-        self.attach(self.execute_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+        self.attach(self.execute_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('BROWSE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('BROWSE'), 0, 1, 5, 6, gtk.FILL, gtk.FILL)
         self.browse_combo = gtk.combo_box_new_text()
         add_options(self.browse_combo)
-        self.attach(self.browse_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
+        self.attach(self.browse_combo, 1, 2, 5, 6, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('DELETE'), 0, 1, 5, 6, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('DELETE'), 0, 1, 6, 7, gtk.FILL, gtk.FILL)
         self.delete_combo = gtk.combo_box_new_text()
         add_options(self.delete_combo)
-        self.attach(self.delete_combo, 1, 2, 5, 6, yoptions = gtk.FILL)
+        self.attach(self.delete_combo, 1, 2, 6, 7, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('WRITE'), 0, 1, 6, 7, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('WRITE'), 0, 1, 7, 8, gtk.FILL, gtk.FILL)
         self.write_combo = gtk.combo_box_new_text()
         add_options(self.write_combo)
-        self.attach(self.write_combo, 1, 2, 6, 7, yoptions = gtk.FILL)
+        self.attach(self.write_combo, 1, 2, 7, 8, yoptions = gtk.FILL)
 
 class ConditionTable(gtk.Table):
     def __init__(self, security_tree):
-        gtk.Table.__init__(self, 5, 3)
+        gtk.Table.__init__(self, 6, 3)
+
+        def remove_element(button, tree):
+            tree.remove_element()
+        button = gtk.Button('Remove this element')
+        button.connect('clicked', remove_element, security_tree)
+        self.attach(button, 0, 2, 0, 1, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('name'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('name'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.name_entry = gtk.Entry()
-        self.attach(self.name_entry, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.attach(self.name_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.name_check = gtk.CheckButton()
-        self.attach(self.name_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(self.name_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
-        self.attach(gtk.Label('value'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('value'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
         self.value_entry = gtk.Entry()
-        self.attach(self.value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(self.value_entry, 1, 2, 2, 3, yoptions = gtk.FILL)
         self.value_check = gtk.CheckButton()
-        self.attach(self.value_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(self.value_check, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
 
         def add_options(combo):
             combo.append_text('Yes')
             combo.append_text('No')
             combo.append_text('empty')
 
-        self.attach(gtk.Label('regex'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('regex'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
         self.regex_combo = gtk.combo_box_new_text()
         add_options(self.regex_combo)
-        self.attach(self.regex_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+        self.attach(self.regex_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('reverse'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('reverse'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
         self.reverse_combo = gtk.combo_box_new_text()
         add_options(self.reverse_combo)
-        self.attach(self.reverse_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+        self.attach(self.reverse_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
 
         def add_sub_element(button, combo, tree):
             tag = combo.get_model()[combo.get_active()][0]
             tree.add_sub_element(tag)
         self.add_sub_element_button = gtk.Button('Add sub-element')
-        self.attach(self.add_sub_element_button, 0, 1, 4, 5, gtk.FILL, 
gtk.FILL)
+        self.attach(self.add_sub_element_button, 0, 1, 5, 6, gtk.FILL, 
gtk.FILL)
         self.add_sub_element_combo = gtk.combo_box_new_text()
         self.add_sub_element_combo.append_text('USER')
         self.add_sub_element_combo.append_text('CONDITION')
@@ -421,86 +439,104 @@ class ConditionTable(gtk.Table):
         self.add_sub_element_combo.append_text('DEFINE element')
         self.add_sub_element_combo.append_text('DEFINE tree')
         self.add_sub_element_combo.set_active(0)
-        self.attach(self.add_sub_element_combo, 1, 2, 4, 5, gtk.FILL, gtk.FILL)
+        self.attach(self.add_sub_element_combo, 1, 2, 5, 6, gtk.FILL, gtk.FILL)
         self.add_sub_element_button.connect('clicked', add_sub_element,
                                             self.add_sub_element_combo,
                                             security_tree)
 
 class PermissionTable(gtk.Table):
-    def __init__(self):
-        gtk.Table.__init__(self, 5, 2)
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 6, 2)
+
+        def remove_element(button, tree):
+            tree.remove_element()
+        button = gtk.Button('Remove this element')
+        button.connect('clicked', remove_element, security_tree)
+        self.attach(button, 0, 2, 0, 1, yoptions = gtk.FILL)
 
         def add_options(combo):
             combo.append_text('Yes')
             combo.append_text('No')
             combo.append_text('empty')
 
-        self.attach(gtk.Label('READ'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('READ'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.read_combo = gtk.combo_box_new_text()
         add_options(self.read_combo)
-        self.attach(self.read_combo, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.attach(self.read_combo, 1, 2, 1, 2, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('EXECUTE'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('EXECUTE'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
         self.execute_combo = gtk.combo_box_new_text()
         add_options(self.execute_combo)
-        self.attach(self.execute_combo, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(self.execute_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('BROWSE'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('BROWSE'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
         self.browse_combo = gtk.combo_box_new_text()
         add_options(self.browse_combo)
-        self.attach(self.browse_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+        self.attach(self.browse_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('DELETE'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('DELETE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
         self.delete_combo = gtk.combo_box_new_text()
         add_options(self.delete_combo)
-        self.attach(self.delete_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+        self.attach(self.delete_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('WRITE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('WRITE'), 0, 1, 5, 6, gtk.FILL, gtk.FILL)
         self.write_combo = gtk.combo_box_new_text()
         add_options(self.write_combo)
-        self.attach(self.write_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
+        self.attach(self.write_combo, 1, 2, 5, 6, yoptions = gtk.FILL)
 
 class ReturnTable(gtk.Table):
-    def __init__(self):
-        gtk.Table.__init__(self, 1, 2)
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 2, 2)
+
+        def remove_element(button, tree):
+            tree.remove_element()
+        button = gtk.Button('Remove this element')
+        button.connect('clicked', remove_element, security_tree)
+        self.attach(button, 0, 2, 0, 1, yoptions = gtk.FILL)
 
-        self.attach(gtk.Label('value'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('value'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.value_combo = gtk.combo_box_new_text()
         self.value_combo.append_text('ALLOW')
         self.value_combo.append_text('DENY')
         self.value_combo.append_text('empty')
-        self.attach(self.value_combo, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.attach(self.value_combo, 1, 2, 1, 2, yoptions = gtk.FILL)
 
 class DefinitionTable(gtk.Table):
     def __init__(self, security_tree):
-        gtk.Table.__init__(self, 6, 3)
+        gtk.Table.__init__(self, 7, 3)
+
+        def remove_element(button, tree):
+            tree.remove_element()
+        button = gtk.Button('Remove this element')
+        button.connect('clicked', remove_element, security_tree)
+        self.attach(button, 0, 2, 0, 1, yoptions = gtk.FILL)
 
         def add_sub_element(button, combo, tree):
             tag = combo.get_model()[combo.get_active()][0]
             tree.add_sub_element(tag)
         self.add_sub_element_button = gtk.Button('Add sub-element')
-        self.attach(self.add_sub_element_button, 0, 1, 0, 1, gtk.FILL, 
gtk.FILL)
+        self.attach(self.add_sub_element_button, 0, 1, 1, 2, gtk.FILL, 
gtk.FILL)
         self.add_sub_element_combo = gtk.combo_box_new_text()
         self.add_sub_element_combo.append_text('DEFINE element')
         self.add_sub_element_combo.append_text('DEFINE tree')
         self.add_sub_element_combo.set_active(0)
-        self.attach(self.add_sub_element_combo, 1, 2, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(self.add_sub_element_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
         self.add_sub_element_button.connect('clicked', add_sub_element,
                                             self.add_sub_element_combo,
                                             security_tree)
 
-        self.attach(gtk.Label('name'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(gtk.Label('name'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
         self.name_entry = gtk.Entry()
-        self.attach(self.name_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(self.name_entry, 1, 2, 2, 3, yoptions = gtk.FILL)
         self.name_check = gtk.CheckButton()
-        self.attach(self.name_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(self.name_check, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
 
         self.value_label = gtk.Label('value')
-        self.attach(self.value_label, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.attach(self.value_label, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
         self.value_entry = gtk.Entry()
-        self.attach(self.value_entry, 1, 2, 2, 3, yoptions = gtk.FILL)
+        self.attach(self.value_entry, 1, 2, 3, 4, yoptions = gtk.FILL)
         self.value_check = gtk.CheckButton()
-        self.attach(self.value_check, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
+        self.attach(self.value_check, 2, 3, 3, 4, gtk.FILL, gtk.FILL)
 
         self.attribute_tree = gtk.TreeView(gtk.ListStore(
                 gobject.TYPE_STRING, # variable
@@ -528,13 +564,13 @@ class DefinitionTable(gtk.Table):
         scroll.set_shadow_type(gtk.SHADOW_OUT)
         scroll.set_border_width(5)
         scroll.add(self.attribute_tree)
-        self.attach(scroll, 0, 2, 5, 6)
+        self.attach(scroll, 0, 2, 6, 7)
 
         def add_attribute(button, model):
             model.append(('', '', ))
         add_button = gtk.Button('add')
         add_button.connect('clicked', add_attribute, model)
-        self.attach(add_button, 0, 2, 3, 4, gtk.FILL, gtk.FILL)
+        self.attach(add_button, 0, 2, 4, 5, gtk.FILL, gtk.FILL)
 
         def remove_attribute(button, tree):
             model, selected = tree.get_selection().get_selected()
@@ -542,7 +578,7 @@ class DefinitionTable(gtk.Table):
                 model.remove(selected)
         remove_button = gtk.Button('remove')
         remove_button.connect('clicked', remove_attribute, self.attribute_tree)
-        self.attach(remove_button, 0, 2, 4, 5, gtk.FILL, gtk.FILL)
+        self.attach(remove_button, 0, 2, 5, 6, gtk.FILL, gtk.FILL)
 
     def set_tree(self, tree):
         if tree:
@@ -591,10 +627,10 @@ class SecurityTable(gtk.Table):
         self.security_tree = SecurityTree(self)
 
         self.empty_table = EmptyTable(self.security_tree)
-        self.user_table = UserTable()
+        self.user_table = UserTable(self.security_tree)
         self.condition_table = ConditionTable(self.security_tree)
-        self.permission_table = PermissionTable()
-        self.return_table = ReturnTable()
+        self.permission_table = PermissionTable(self.security_tree)
+        self.return_table = ReturnTable(self.security_tree)
         self.definition_table = DefinitionTable(self.security_tree)
         self.table_notebook = gtk.Notebook()
         self.table_notebook.append_page(self.empty_table)



commit ebe91b6cdd15d5f51c086aebb1dae4a2817b8ec9
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Aug 22 23:24:23 2009 +0200

    GUI can save security files.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 928dcfa..faf314b 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -443,15 +443,19 @@ class PyGTKControl():
     def construct_browser(self):
         '''Constructs file browser.'''
         panels = gtk.HPaned()
-        security_table = SecurityTable()
-        panels.pack2(security_table, True, False)
-        browser_table = BrowserTable(security_table.read_from_file)
-        panels.pack1(browser_table, True, False)
+        self.security_table = SecurityTable()
+        panels.pack2(self.security_table, True, False)
+        self.browser_table = BrowserTable(self.security_table.read_from_file)
+        panels.pack1(self.browser_table, True, False)
         
         self.widgets.get_widget('notebook').append_page(
             panels, gtk.Label('File browser'))
 
         self.widgets.get_widget('notebook').show_all()
 
+    def on_save_security_menu_item_activate(self, widget):
+        self.security_table.write_to_file(
+            self.browser_table.browser_tree.browser)
+
 PyGTKControl()
 gtk.main()
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 63db0cc..fb843a0 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -488,6 +488,33 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkMenuItem" id="security_menu_item">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Security</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="security_menu">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="save_security_menu_item">
+                        <property name="label">Save security</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_save_security_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="save_security_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-save</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>
diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index 1f5ab86..b5f6649 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -288,6 +288,49 @@ class SecurityTree(gtk.TreeView):
             add = DefinitionTree()
         model.append(selected, (tag, add, ))
 
+    def export(self):
+        def add_definition_tree(parent, i, model):
+            while len(parent.get_definitions()): # remove all children
+                parent.remove_definition(0)
+            i = model.iter_children(i)
+            while i is not None:
+                tag = model.get_value(i, 0)
+                data = model.get_value(i, 1)
+                parent.add_definition(data)
+                if tag == 'DEFINE tree':
+                    add_definition_tree(data, i, model)
+                i = model.iter_next(i)
+        def add_condition(parent, i, model):
+            while len(parent.get_sub_elements()): # remove all children
+                parent.remove_sub_element(0)
+            i = model.iter_children(i)
+            while i is not None:
+                tag = model.get_value(i, 0)
+                data = model.get_value(i, 1)
+                parent.add_sub_element(data)
+                if tag == 'DEFINE tree':
+                    add_definition_tree(data, i, model)
+                elif tag == 'CONDITION':
+                    add_condition(data, i, model)
+                i = model.iter_next(i)
+        self.save()
+        model = self.get_model()
+        i = model.iter_children(None)
+        security = model.get_value(i, 1)
+        while len(security.get_elements()): # remove all children
+            security.remove_element(0)
+        i = model.iter_children(i)
+        while i is not None:
+            tag = model.get_value(i, 0)
+            data = model.get_value(i, 1)
+            security.add_element(data)
+            if tag == 'DEFINE tree':
+                add_definition_tree(data, i, model)
+            elif tag == 'CONDITION':
+                add_condition(data, i, model)
+            i = model.iter_next(i)
+        return security
+
 class UserTable(gtk.Table):
     def __init__(self):
         gtk.Table.__init__(self, 7, 3)
@@ -431,7 +474,7 @@ class ReturnTable(gtk.Table):
 class DefinitionTable(gtk.Table):
     def __init__(self, security_tree):
         gtk.Table.__init__(self, 6, 3)
-        
+
         def add_sub_element(button, combo, tree):
             tag = combo.get_model()[combo.get_active()][0]
             tree.add_sub_element(tag)
@@ -522,7 +565,7 @@ class EmptyTable(gtk.Table):
         self.attach(gtk.Label('Use security file'), 0, 1, 0, 1, gtk.FILL, 
gtk.FILL)
         self.security_check = gtk.CheckButton()
         self.attach(self.security_check, 1, 2, 0, 1, yoptions = gtk.FILL)
-        
+
         def add_sub_element(button, combo, tree):
             tag = combo.get_model()[combo.get_active()][0]
             tree.add_sub_element(tag)
@@ -546,7 +589,7 @@ class SecurityTable(gtk.Table):
         gtk.Table.__init__(self, 1, 2)
 
         self.security_tree = SecurityTree(self)
-        
+
         self.empty_table = EmptyTable(self.security_tree)
         self.user_table = UserTable()
         self.condition_table = ConditionTable(self.security_tree)
@@ -575,6 +618,16 @@ class SecurityTable(gtk.Table):
         except IOError:
             self.security_tree.clear()
 
+    def write_to_file(self, browser):
+        if self.empty_table.security_check.get_active():
+            security = self.security_tree.export()
+            browser.put_file('security.xml', str(security))
+        else:
+            try:
+                browser.remove_file('security.xml')
+            except OSError: # file does not exist
+                pass
+
     def switch_table(self, tag):
         if tag == 'SECURITY':
             self.current_table = self.empty_table
diff --git a/misc/py_control_client/MyServer/pycontrollib/browser.py 
b/misc/py_control_client/MyServer/pycontrollib/browser.py
index 63b8317..82c0456 100644
--- a/misc/py_control_client/MyServer/pycontrollib/browser.py
+++ b/misc/py_control_client/MyServer/pycontrollib/browser.py
@@ -31,6 +31,9 @@ class FileBrowser():
     
     def put_file(self, path, text):
         raise NotImplementedError()
+
+    def remove_file(self, path):
+        raise NotImplementedError()
     
     def get_path(self):
         raise NotImplementedError()
@@ -65,7 +68,10 @@ class LocalFileBrowser(FileBrowser):
 
     def put_file(self, path, text):
         with open(os.path.join(self.path, path), 'w') as f:
-            f.write(text) 
+            f.write(text)
+
+    def remove_file(self, path):
+        os.remove(os.path.join(self.path, path))
 
     def get_path(self):
         return self.path



commit 1fe17cec41609d5dcc35e494f6b9e9d6cc190161
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Aug 21 23:53:46 2009 +0200

    Full security file edition support in GUI.
    
    Save is still missing.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 93589d7..928dcfa 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -443,10 +443,10 @@ class PyGTKControl():
     def construct_browser(self):
         '''Constructs file browser.'''
         panels = gtk.HPaned()
-        browser_table = BrowserTable()
-        panels.pack1(browser_table, True, False)
         security_table = SecurityTable()
         panels.pack2(security_table, True, False)
+        browser_table = BrowserTable(security_table.read_from_file)
+        panels.pack1(browser_table, True, False)
         
         self.widgets.get_widget('notebook').append_page(
             panels, gtk.Label('File browser'))
diff --git a/misc/py_control_client/MyServer/GUI/BrowserWidgets.py 
b/misc/py_control_client/MyServer/GUI/BrowserWidgets.py
index 4d540fc..219ef16 100644
--- a/misc/py_control_client/MyServer/GUI/BrowserWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/BrowserWidgets.py
@@ -22,10 +22,11 @@ import gobject
 from MyServer.pycontrollib.browser import LocalFileBrowser
 
 class BrowserTreeView(gtk.TreeView):
-    def __init__(self):
+    def __init__(self, callback):
         gtk.TreeView.__init__(self, gtk.ListStore(
                 gdk.Pixbuf,
                 gobject.TYPE_STRING))
+        self.callback = callback
         self.model = self.get_model()
         renderer = gtk.CellRendererPixbuf()
         column = gtk.TreeViewColumn()
@@ -44,6 +45,7 @@ class BrowserTreeView(gtk.TreeView):
         self.dir_icon = icon_theme.load_icon('gtk-directory', 16, 0)
         self.update()
         self.connect('row-activated', self.change_dir)
+        callback(self.browser)
 
         self.scroll = gtk.ScrolledWindow()
         self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -60,12 +62,13 @@ class BrowserTreeView(gtk.TreeView):
     def change_dir(self, widget, path, view_column):
         self.browser.change_dir(self.model[path][1])
         self.update()
+        self.callback(self.browser)
 
 class BrowserTable(gtk.Table):
-    def __init__(self):
+    def __init__(self, callback):
         gtk.Table.__init__(self, 2, 1)
 
-        self.browser_tree = BrowserTreeView()
+        self.browser_tree = BrowserTreeView(callback)
 
         def set_show_hidden(button):
             self.browser_tree.browser.show_hidden(button.get_active())
diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index e8036a1..1f5ab86 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -20,7 +20,7 @@ import gtk
 import gobject
 from MyServer.pycontrollib.security import SecurityList, User, Return, \
     Condition, Permission
-from MyServer.pycontrollib.definition import DefinitionElement
+from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 
 class SecurityTree(gtk.TreeView):
     def __init__(self, security_table):
@@ -44,25 +44,36 @@ class SecurityTree(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
-        self.clear()
-
     def clear(self):
         self.get_model().clear()
+        self.last_selected = None
         self.get_model().append(None, ('SECURITY', SecurityList(), ))
+        self.security_table.empty_table.security_check.set_active(False)
+        self.security_table.switch_table('SECURITY')
 
     def set_up(self, security_list):
+        def add_from_definition_tree(parent, model, definition):
+            for element in definition.get_definitions():
+                x = model.append(parent, (element.tag, element, ))
+                if element.tag == 'DEFINE tree':
+                    add_from_definition_tree(x, model, element)
         def add_from_condition(parent, model, condition):
             for element in condition.get_sub_elements():
                 x = model.append(parent, (element.tag, element, ))
                 if element.tag == 'CONDITION':
                     add_from_condition(x, model, element)
+        self.security_table.empty_table.security_check.set_active(True)
+        self.security_table.switch_table('SECURITY')
         model = self.get_model()
+        self.last_selected = None
         model.clear()
         parent = model.append(None, ('SECURITY', security_list))
         for element in security_list.get_elements():
             x = model.append(parent, (element.tag, element, ))
             if element.tag == 'CONDITION':
                 add_from_condition(x, model, element)
+            elif element.tag == 'DEFINE tree':
+                add_from_definition_tree(x, model, element)
 
     def save(self):
         if self.last_selected is None:
@@ -134,6 +145,27 @@ class SecurityTree(gtk.TreeView):
             combo = table.value_combo
             value = combo.get_model()[combo.get_active()][0]
             data.set_value(value if value != 'empty' else None)
+        elif tag == 'DEFINE element' or tag == 'DEFINE tree':
+            table = self.security_table.definition_table
+            if table.name_check.get_active():
+                data.set_name(table.name_entry.get_text())
+            else:
+                data.set_name(None)
+            variables = []
+            for variable in data.get_attributes():
+                variables.append(variable)
+            for variable in variables:
+                data.remove_attribute(variable)
+            model = table.attribute_tree.get_model()
+            i = model.iter_children(None)
+            while i is not None: # iterate over attributes
+                data.set_attribute(
+                    model.get_value(i, 0),
+                    model.get_value(i, 1))
+                i = model.iter_next(i)
+            if tag == 'DEFINE element':
+                if table.value_check.get_active():
+                    data.set_attribute('value', table.value_entry.get_text())
 
     def update_gui(self, tag, data):
         if tag == 'USER':
@@ -207,6 +239,26 @@ class SecurityTree(gtk.TreeView):
             combo = table.value_combo
             value =  2 if data.get_value() is None else int(data.get_value() 
== 'DENY')
             combo.set_active(value)
+        elif tag == 'DEFINE element' or tag == 'DEFINE tree':
+            table = self.security_table.definition_table
+            model = table.attribute_tree.get_model()
+            model.clear()
+            for variable, value in data.get_attributes().iteritems():
+                if variable != 'value':
+                    model.append((variable, value, ))
+            if data.get_name() is None:
+                table.name_check.set_active(False)
+                table.name_entry.set_text('')
+            else:
+                table.name_check.set_active(True)
+                table.name_entry.set_text(data.get_name())
+            if tag == 'DEFINE element':
+                try:
+                    table.value_check.set_active(True)
+                    table.value_entry.set_text(data.get_attribute('value'))
+                except KeyError:
+                    table.value_check.set_active(False)
+                    table.value_entry.set_text('')
 
     def cursor_changed(self, tree):
         self.save()
@@ -220,6 +272,8 @@ class SecurityTree(gtk.TreeView):
 
     def add_sub_element(self, tag):
         model, selected = self.get_selection().get_selected()
+        if selected is None:
+            return
         if tag == 'USER':
             add = User()
         elif tag == 'RETURN':
@@ -228,6 +282,10 @@ class SecurityTree(gtk.TreeView):
             add = Condition()
         elif tag == 'PERMISSION':
             add = Permission()
+        elif tag == 'DEFINE element':
+            add = DefinitionElement()
+        elif tag == 'DEFINE tree':
+            add = DefinitionTree()
         model.append(selected, (tag, add, ))
 
 class UserTable(gtk.Table):
@@ -290,7 +348,7 @@ class ConditionTable(gtk.Table):
         self.value_entry = gtk.Entry()
         self.attach(self.value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.value_check = gtk.CheckButton()
-        self.attach(self.value_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(self.value_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
         def add_options(combo):
             combo.append_text('Yes')
@@ -317,7 +375,8 @@ class ConditionTable(gtk.Table):
         self.add_sub_element_combo.append_text('CONDITION')
         self.add_sub_element_combo.append_text('PERMISSION')
         self.add_sub_element_combo.append_text('RETURN')
-        self.add_sub_element_combo.append_text('DEFINE')
+        self.add_sub_element_combo.append_text('DEFINE element')
+        self.add_sub_element_combo.append_text('DEFINE tree')
         self.add_sub_element_combo.set_active(0)
         self.attach(self.add_sub_element_combo, 1, 2, 4, 5, gtk.FILL, gtk.FILL)
         self.add_sub_element_button.connect('clicked', add_sub_element,
@@ -370,8 +429,91 @@ class ReturnTable(gtk.Table):
         self.attach(self.value_combo, 1, 2, 0, 1, yoptions = gtk.FILL)
 
 class DefinitionTable(gtk.Table):
-    def __init__(self):
-        gtk.Table.__init__(self, 1, 2)
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 6, 3)
+        
+        def add_sub_element(button, combo, tree):
+            tag = combo.get_model()[combo.get_active()][0]
+            tree.add_sub_element(tag)
+        self.add_sub_element_button = gtk.Button('Add sub-element')
+        self.attach(self.add_sub_element_button, 0, 1, 0, 1, gtk.FILL, 
gtk.FILL)
+        self.add_sub_element_combo = gtk.combo_box_new_text()
+        self.add_sub_element_combo.append_text('DEFINE element')
+        self.add_sub_element_combo.append_text('DEFINE tree')
+        self.add_sub_element_combo.set_active(0)
+        self.attach(self.add_sub_element_combo, 1, 2, 0, 1, gtk.FILL, gtk.FILL)
+        self.add_sub_element_button.connect('clicked', add_sub_element,
+                                            self.add_sub_element_combo,
+                                            security_tree)
+
+        self.attach(gtk.Label('name'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.name_entry = gtk.Entry()
+        self.attach(self.name_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.name_check = gtk.CheckButton()
+        self.attach(self.name_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+
+        self.value_label = gtk.Label('value')
+        self.attach(self.value_label, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.value_entry = gtk.Entry()
+        self.attach(self.value_entry, 1, 2, 2, 3, yoptions = gtk.FILL)
+        self.value_check = gtk.CheckButton()
+        self.attach(self.value_check, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
+
+        self.attribute_tree = gtk.TreeView(gtk.ListStore(
+                gobject.TYPE_STRING, # variable
+                gobject.TYPE_STRING)) # value
+        model = self.attribute_tree.get_model()
+        def edited_handler(cell, path, text, data):
+            model, col = data
+            model[path][col] = text
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        renderer.connect('edited', edited_handler, (model, 0, ))
+        column = gtk.TreeViewColumn('variable')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 0)
+        self.attribute_tree.append_column(column)
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        renderer.connect('edited', edited_handler, (model, 1, ))
+        column = gtk.TreeViewColumn('value')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 1)
+        self.attribute_tree.append_column(column)
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scroll.set_shadow_type(gtk.SHADOW_OUT)
+        scroll.set_border_width(5)
+        scroll.add(self.attribute_tree)
+        self.attach(scroll, 0, 2, 5, 6)
+
+        def add_attribute(button, model):
+            model.append(('', '', ))
+        add_button = gtk.Button('add')
+        add_button.connect('clicked', add_attribute, model)
+        self.attach(add_button, 0, 2, 3, 4, gtk.FILL, gtk.FILL)
+
+        def remove_attribute(button, tree):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None:
+                model.remove(selected)
+        remove_button = gtk.Button('remove')
+        remove_button.connect('clicked', remove_attribute, self.attribute_tree)
+        self.attach(remove_button, 0, 2, 4, 5, gtk.FILL, gtk.FILL)
+
+    def set_tree(self, tree):
+        if tree:
+            self.add_sub_element_button.show()
+            self.add_sub_element_combo.show()
+            self.value_label.hide()
+            self.value_entry.hide()
+            self.value_check.hide()
+        else:
+            self.add_sub_element_button.hide()
+            self.add_sub_element_combo.hide()
+            self.value_label.show()
+            self.value_entry.show()
+            self.value_check.show()
 
 class EmptyTable(gtk.Table):
     def __init__(self, security_tree):
@@ -391,7 +533,8 @@ class EmptyTable(gtk.Table):
         self.add_sub_element_combo.append_text('CONDITION')
         self.add_sub_element_combo.append_text('PERMISSION')
         self.add_sub_element_combo.append_text('RETURN')
-        self.add_sub_element_combo.append_text('DEFINE')
+        self.add_sub_element_combo.append_text('DEFINE element')
+        self.add_sub_element_combo.append_text('DEFINE tree')
         self.add_sub_element_combo.set_active(0)
         self.attach(self.add_sub_element_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
         self.add_sub_element_button.connect('clicked', add_sub_element,
@@ -403,40 +546,56 @@ class SecurityTable(gtk.Table):
         gtk.Table.__init__(self, 1, 2)
 
         self.security_tree = SecurityTree(self)
-
+        
+        self.empty_table = EmptyTable(self.security_tree)
         self.user_table = UserTable()
         self.condition_table = ConditionTable(self.security_tree)
         self.permission_table = PermissionTable()
         self.return_table = ReturnTable()
-        self.definition_table = DefinitionTable()
-        self.empty_table = EmptyTable(self.security_tree)
-        self.current_table = self.empty_table
+        self.definition_table = DefinitionTable(self.security_tree)
+        self.table_notebook = gtk.Notebook()
+        self.table_notebook.append_page(self.empty_table)
+        self.table_notebook.append_page(self.user_table)
+        self.table_notebook.append_page(self.condition_table)
+        self.table_notebook.append_page(self.permission_table)
+        self.table_notebook.append_page(self.return_table)
+        self.table_notebook.append_page(self.definition_table)
+        self.table_notebook.set_show_tabs(False)
 
         self.attach(self.security_tree.scroll, 0, 1, 0, 1)
-        self.attach(self.current_table, 1, 2, 0, 1, gtk.FILL)
+        self.attach(self.table_notebook, 1, 2, 0, 1, gtk.FILL)
 
-    def read_from_file(self, file_name):
+    def read_from_file(self, browser):
         self.switch_table('SECURITY')
+        self.security_tree.clear()
         try:
-            f = open(file_name)
-            security = SecurityList.from_string(f.read())
+            security = SecurityList.from_string(
+                browser.get_file('security.xml'))
             self.security_tree.set_up(security)
-            f.close()
         except IOError:
             self.security_tree.clear()
 
-    def switch_table(self, tag): # switch doesn't work
-        self.remove(self.current_table)
-        if tag == 'USER':
+    def switch_table(self, tag):
+        if tag == 'SECURITY':
+            self.current_table = self.empty_table
+            self.table_notebook.set_current_page(0)
+        elif tag == 'USER':
             self.current_table = self.user_table
+            self.table_notebook.set_current_page(1)
         elif tag == 'CONDITION':
             self.current_table = self.condition_table
+            self.table_notebook.set_current_page(2)
         elif tag == 'PERMISSION':
             self.current_table = self.permission_table
+            self.table_notebook.set_current_page(3)
         elif tag == 'RETURN':
             self.current_table = self.return_table
-        elif tag == 'DEFINITION':
+            self.table_notebook.set_current_page(4)
+        elif tag == 'DEFINE element':
             self.current_table = self.definition_table
-        elif tag == 'SECURITY':
-            self.current_table = self.empty_table
-        self.attach(self.current_table, 1, 2, 0, 1, gtk.FILL)
+            self.definition_table.set_tree(False)
+            self.table_notebook.set_current_page(5)
+        elif tag == 'DEFINE tree':
+            self.current_table = self.definition_table
+            self.definition_table.set_tree(True)
+            self.table_notebook.set_current_page(5)
diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index b8e5b03..9ac2f55 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -82,6 +82,7 @@ class DefinitionElement(Definition):
     def __init__(self, name = None, attributes = {}):
         '''Creates new definition element with given name and attributes.'''
         Definition.__init__(self, name)
+        self.tag = 'DEFINE element'
         for key, value in attributes.iteritems():
             self.set_attribute(key, value)
 
@@ -122,7 +123,7 @@ class DefinitionElement(Definition):
     def from_string(text):
         '''Factory to produce definition element by parsing a string.'''
         return DefinitionElement.from_lxml_element(etree.XML(text))
-    
+
     def search_by_name(self, name):
         return None if name != self.name else self
 
@@ -133,6 +134,7 @@ class DefinitionTree(Definition):
         '''Creates new definition tree with given name, sub-definitions and
         attributes. values is expected to be iterable.'''
         Definition.__init__(self, name, attributes)
+        self.tag = 'DEFINE tree'
         self.definitions = []
         self.custom = [] # list of children not being definitions
         for definition in definitions:
@@ -196,7 +198,7 @@ class DefinitionTree(Definition):
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
-    
+
     def search_by_name(self, name):
         for definition in reversed(self.definitions):
             ret = definition.search_by_name(name)



commit 4d884698e00d9fdd70b6d09bd52c6662ccc78153
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 19 12:43:36 2009 +0200

    Implemented opening of security files.

diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index db45675..e8036a1 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -18,7 +18,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 
 import gtk
 import gobject
-from MyServer.pycontrollib.security import SecurityElement, User, Return, \
+from MyServer.pycontrollib.security import SecurityList, User, Return, \
     Condition, Permission
 from MyServer.pycontrollib.definition import DefinitionElement
 
@@ -44,11 +44,25 @@ class SecurityTree(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
-        x = self.get_model().append(None, ('SECURITY', SecurityElement(), ))
-        self.get_model().append(x, ('USER', User(), ))
-        self.get_model().append(x, ('USER', User(), ))
-        self.get_model().append(x, ('RETURN', Return(), ))
-        self.get_model().append(x, ('CONDITION', Condition(), ))
+        self.clear()
+
+    def clear(self):
+        self.get_model().clear()
+        self.get_model().append(None, ('SECURITY', SecurityList(), ))
+
+    def set_up(self, security_list):
+        def add_from_condition(parent, model, condition):
+            for element in condition.get_sub_elements():
+                x = model.append(parent, (element.tag, element, ))
+                if element.tag == 'CONDITION':
+                    add_from_condition(x, model, element)
+        model = self.get_model()
+        model.clear()
+        parent = model.append(None, ('SECURITY', security_list))
+        for element in security_list.get_elements():
+            x = model.append(parent, (element.tag, element, ))
+            if element.tag == 'CONDITION':
+                add_from_condition(x, model, element)
 
     def save(self):
         if self.last_selected is None:
@@ -388,19 +402,29 @@ class SecurityTable(gtk.Table):
     def __init__(self):
         gtk.Table.__init__(self, 1, 2)
 
-        security_tree = SecurityTree(self)
+        self.security_tree = SecurityTree(self)
 
         self.user_table = UserTable()
-        self.condition_table = ConditionTable(security_tree)
+        self.condition_table = ConditionTable(self.security_tree)
         self.permission_table = PermissionTable()
         self.return_table = ReturnTable()
         self.definition_table = DefinitionTable()
-        self.empty_table = EmptyTable(security_tree)
+        self.empty_table = EmptyTable(self.security_tree)
         self.current_table = self.empty_table
 
-        self.attach(security_tree.scroll, 0, 1, 0, 1)
+        self.attach(self.security_tree.scroll, 0, 1, 0, 1)
         self.attach(self.current_table, 1, 2, 0, 1, gtk.FILL)
 
+    def read_from_file(self, file_name):
+        self.switch_table('SECURITY')
+        try:
+            f = open(file_name)
+            security = SecurityList.from_string(f.read())
+            self.security_tree.set_up(security)
+            f.close()
+        except IOError:
+            self.security_tree.clear()
+
     def switch_table(self, tag): # switch doesn't work
         self.remove(self.current_table)
         if tag == 'USER':
diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index aefaf57..74e0dc1 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -47,6 +47,7 @@ class SecurityElement():
 class Condition(SecurityElement):
     def __init__(self, name = None, value = None, reverse = None, regex = None,
                  sub_elements = []):
+        self.tag = 'CONDITION'
         self.set_name(name)
         self.set_value(value)
         self.set_reverse(reverse)
@@ -157,6 +158,7 @@ class Condition(SecurityElement):
 class Permission(SecurityElement):
     def __init__(self, read = None, execute = None, browse = None,
                  delete = None, write = None):
+        self.tag = 'PERMISSION'
         self.set_read(read)
         self.set_execute(execute)
         self.set_browse(browse)
@@ -252,6 +254,7 @@ class Permission(SecurityElement):
 class User(SecurityElement):
     def __init__(self, name = None, password = None, read = None,
                  execute = None, browse = None, delete = None, write = None):
+        self.tag = 'USER'
         self.set_name(name)
         self.set_password(password)
         self.set_read(read)
@@ -368,6 +371,7 @@ class User(SecurityElement):
 
 class Return(SecurityElement):
     def __init__(self, value = None):
+        self.tag = 'RETURN'
         self.set_value(value)
         self.custom_attrib = {}
         self.custom = []



commit 1e783e320be4a0858adc2ae11b354a2736338586
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 18 21:10:19 2009 +0200

    Security editor actually works.

diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
index c6b5021..db45675 100644
--- a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -18,6 +18,9 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 
 import gtk
 import gobject
+from MyServer.pycontrollib.security import SecurityElement, User, Return, \
+    Condition, Permission
+from MyServer.pycontrollib.definition import DefinitionElement
 
 class SecurityTree(gtk.TreeView):
     def __init__(self, security_table):
@@ -32,6 +35,7 @@ class SecurityTree(gtk.TreeView):
 
         self.security_table = security_table
 
+        self.last_selected = None
         self.connect('cursor-changed', self.cursor_changed)
 
         self.scroll = gtk.ScrolledWindow()
@@ -40,19 +44,177 @@ class SecurityTree(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
-        x = self.get_model().append(None, ('SECURITY', '', ))
-        self.get_model().append(x, ('USER', '', ))
-        self.get_model().append(x, ('RETURN', '', ))
-        self.get_model().append(x, ('CONDITION', '', ))
+        x = self.get_model().append(None, ('SECURITY', SecurityElement(), ))
+        self.get_model().append(x, ('USER', User(), ))
+        self.get_model().append(x, ('USER', User(), ))
+        self.get_model().append(x, ('RETURN', Return(), ))
+        self.get_model().append(x, ('CONDITION', Condition(), ))
+
+    def save(self):
+        if self.last_selected is None:
+            return
+        model = self.get_model()
+        row = model[self.last_selected]
+        tag = row[0]
+        data = row[1]
+        if tag == 'USER':
+            table = self.security_table.user_table
+            if table.name_check.get_active():
+                data.set_name(table.name_entry.get_text())
+            else:
+                data.set_name(None)
+            if table.password_check.get_active():
+                data.set_password(table.password_entry.get_text())
+            else:
+                data.set_password(None)
+            combo = table.read_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_read(value == 'Yes' if value != 'empty' else None)
+            combo = table.execute_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_execute(value == 'Yes' if value != 'empty' else None)
+            combo = table.browse_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_browse(value == 'Yes'if value != 'empty' else None)
+            combo = table.delete_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_delete(value == 'Yes' if value != 'empty' else None)
+            combo = table.write_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_write(value == 'Yes' if value != 'empty' else None)
+        elif tag == 'CONDITION':
+            table = self.security_table.condition_table
+            if table.name_check.get_active():
+                data.set_name(table.name_entry.get_text())
+            else:
+                data.set_name(None)
+            if table.value_check.get_active():
+                data.set_value(table.value_entry.get_text())
+            else:
+                data.set_value(None)
+            combo = table.regex_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_regex(value == 'Yes' if value != 'empty' else None)
+            combo = table.reverse_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_reverse(value == 'Yes' if value != 'empty' else None)
+        elif tag == 'PERMISSION':
+            table = self.security_table.permission_table
+            combo = table.read_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_read(value == 'Yes' if value != 'empty' else None)
+            combo = table.execute_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_execute(value == 'Yes' if value != 'empty' else None)
+            combo = table.browse_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_browse(value == 'Yes'if value != 'empty' else None)
+            combo = table.delete_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_delete(value == 'Yes' if value != 'empty' else None)
+            combo = table.write_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_write(value == 'Yes' if value != 'empty' else None)
+        elif tag == 'RETURN':
+            table = self.security_table.return_table
+            combo = table.value_combo
+            value = combo.get_model()[combo.get_active()][0]
+            data.set_value(value if value != 'empty' else None)
+
+    def update_gui(self, tag, data):
+        if tag == 'USER':
+            table = self.security_table.user_table
+            if data.get_name() is None:
+                table.name_check.set_active(False)
+                table.name_entry.set_text('')
+            else:
+                table.name_check.set_active(True)
+                table.name_entry.set_text(data.get_name())
+            if data.get_password() is None:
+                table.password_check.set_active(False)
+                table.password_entry.set_text('')
+            else:
+                table.password_check.set_active(True)
+                table.password_entry.set_text(data.get_password())
+            combo = table.read_combo
+            value =  2 if data.get_read() is None else int(not data.get_read())
+            combo.set_active(value)
+            combo = table.execute_combo
+            value =  2 if data.get_execute() is None else int(not 
data.get_execute())
+            combo.set_active(value)
+            combo = table.browse_combo
+            value =  2 if data.get_browse() is None else int(not 
data.get_browse())
+            combo.set_active(value)
+            combo = table.delete_combo
+            value =  2 if data.get_delete() is None else int(not 
data.get_delete())
+            combo.set_active(value)
+            combo = table.write_combo
+            value =  2 if data.get_write() is None else int(not 
data.get_write())
+            combo.set_active(value)
+        elif tag == 'CONDITION':
+            table = self.security_table.condition_table
+            if data.get_name() is None:
+                table.name_check.set_active(False)
+                table.name_entry.set_text('')
+            else:
+                table.name_check.set_active(True)
+                table.name_entry.set_text(data.get_name())
+            if data.get_value() is None:
+                table.value_check.set_active(False)
+                table.value_entry.set_text('')
+            else:
+                table.value_check.set_active(True)
+                table.value_entry.set_text(data.get_value())
+            combo = table.regex_combo
+            value =  2 if data.get_regex() is None else int(not 
data.get_regex())
+            combo.set_active(value)
+            combo = table.reverse_combo
+            value =  2 if data.get_reverse() is None else int(not 
data.get_reverse())
+            combo.set_active(value)
+        elif tag == 'PERMISSION':
+            table = self.security_table.permission_table
+            combo = table.read_combo
+            value =  2 if data.get_read() is None else int(not data.get_read())
+            combo.set_active(value)
+            combo = table.execute_combo
+            value =  2 if data.get_execute() is None else int(not 
data.get_execute())
+            combo.set_active(value)
+            combo = table.browse_combo
+            value =  2 if data.get_browse() is None else int(not 
data.get_browse())
+            combo.set_active(value)
+            combo = table.delete_combo
+            value =  2 if data.get_delete() is None else int(not 
data.get_delete())
+            combo.set_active(value)
+            combo = table.write_combo
+            value =  2 if data.get_write() is None else int(not 
data.get_write())
+            combo.set_active(value)
+        elif tag == 'RETURN':
+            table = self.security_table.return_table
+            combo = table.value_combo
+            value =  2 if data.get_value() is None else int(data.get_value() 
== 'DENY')
+            combo.set_active(value)
 
     def cursor_changed(self, tree):
+        self.save()
+
         model, selected = tree.get_selection().get_selected()
+        self.last_selected = selected
         row = model[selected]
         self.security_table.switch_table(row[0])
 
+        self.update_gui(row[0], row[1])
+
     def add_sub_element(self, tag):
         model, selected = self.get_selection().get_selected()
-        model.append(selected, (tag, '', ))
+        if tag == 'USER':
+            add = User()
+        elif tag == 'RETURN':
+            add = Return()
+        elif tag == 'CONDITION':
+            add = Condition()
+        elif tag == 'PERMISSION':
+            add = Permission()
+        model.append(selected, (tag, add, ))
 
 class UserTable(gtk.Table):
     def __init__(self):
@@ -71,8 +233,8 @@ class UserTable(gtk.Table):
         self.attach(self.password_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
         def add_options(combo):
-            combo.append_text('YES')
-            combo.append_text('NO')
+            combo.append_text('Yes')
+            combo.append_text('No')
             combo.append_text('empty')
 
         self.attach(gtk.Label('READ'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
@@ -117,8 +279,8 @@ class ConditionTable(gtk.Table):
         self.attach(self.value_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
 
         def add_options(combo):
-            combo.append_text('yes')
-            combo.append_text('no')
+            combo.append_text('Yes')
+            combo.append_text('No')
             combo.append_text('empty')
 
         self.attach(gtk.Label('regex'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
@@ -153,8 +315,8 @@ class PermissionTable(gtk.Table):
         gtk.Table.__init__(self, 5, 2)
 
         def add_options(combo):
-            combo.append_text('YES')
-            combo.append_text('NO')
+            combo.append_text('Yes')
+            combo.append_text('No')
             combo.append_text('empty')
 
         self.attach(gtk.Label('READ'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)



commit d93d10e52bffcc144bab2a80b57c5c4333912d50
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 18 19:03:39 2009 +0200

    Partial security file edition support.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index c05e312..93589d7 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -31,6 +31,7 @@ from MyServer.GUI.DefinitionWidgets import DefinitionTable, 
DefinitionTreeView
 from MyServer.GUI.MIMEWidgets import MimeTable, MimeTreeView
 from MyServer.GUI.VHostWidgets import VHostTable, VHostTreeView
 from MyServer.GUI.BrowserWidgets import BrowserTable
+from MyServer.GUI.SecurityWidgets import SecurityTable
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -441,10 +442,14 @@ class PyGTKControl():
 
     def construct_browser(self):
         '''Constructs file browser.'''
-        table = BrowserTable()
+        panels = gtk.HPaned()
+        browser_table = BrowserTable()
+        panels.pack1(browser_table, True, False)
+        security_table = SecurityTable()
+        panels.pack2(security_table, True, False)
         
         self.widgets.get_widget('notebook').append_page(
-            table, gtk.Label('File browser'))
+            panels, gtk.Label('File browser'))
 
         self.widgets.get_widget('notebook').show_all()
 
diff --git a/misc/py_control_client/MyServer/GUI/SecurityWidgets.py 
b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
new file mode 100644
index 0000000..c6b5021
--- /dev/null
+++ b/misc/py_control_client/MyServer/GUI/SecurityWidgets.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gobject
+
+class SecurityTree(gtk.TreeView):
+    def __init__(self, security_table):
+        gtk.TreeView.__init__(self, gtk.TreeStore(
+                gobject.TYPE_STRING, # tag
+                gobject.TYPE_PYOBJECT)) # object
+        renderer = gtk.CellRendererText()
+        column = gtk.TreeViewColumn('tag')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 0)
+        self.append_column(column)
+
+        self.security_table = security_table
+
+        self.connect('cursor-changed', self.cursor_changed)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+        x = self.get_model().append(None, ('SECURITY', '', ))
+        self.get_model().append(x, ('USER', '', ))
+        self.get_model().append(x, ('RETURN', '', ))
+        self.get_model().append(x, ('CONDITION', '', ))
+
+    def cursor_changed(self, tree):
+        model, selected = tree.get_selection().get_selected()
+        row = model[selected]
+        self.security_table.switch_table(row[0])
+
+    def add_sub_element(self, tag):
+        model, selected = self.get_selection().get_selected()
+        model.append(selected, (tag, '', ))
+
+class UserTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 7, 3)
+
+        self.attach(gtk.Label('name'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.name_entry = gtk.Entry()
+        self.attach(self.name_entry, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.name_check = gtk.CheckButton()
+        self.attach(self.name_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+
+        self.attach(gtk.Label('password'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.password_entry = gtk.Entry()
+        self.attach(self.password_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.password_check = gtk.CheckButton()
+        self.attach(self.password_check, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+
+        def add_options(combo):
+            combo.append_text('YES')
+            combo.append_text('NO')
+            combo.append_text('empty')
+
+        self.attach(gtk.Label('READ'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.read_combo = gtk.combo_box_new_text()
+        add_options(self.read_combo)
+        self.attach(self.read_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('EXECUTE'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.execute_combo = gtk.combo_box_new_text()
+        add_options(self.execute_combo)
+        self.attach(self.execute_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('BROWSE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
+        self.browse_combo = gtk.combo_box_new_text()
+        add_options(self.browse_combo)
+        self.attach(self.browse_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('DELETE'), 0, 1, 5, 6, gtk.FILL, gtk.FILL)
+        self.delete_combo = gtk.combo_box_new_text()
+        add_options(self.delete_combo)
+        self.attach(self.delete_combo, 1, 2, 5, 6, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('WRITE'), 0, 1, 6, 7, gtk.FILL, gtk.FILL)
+        self.write_combo = gtk.combo_box_new_text()
+        add_options(self.write_combo)
+        self.attach(self.write_combo, 1, 2, 6, 7, yoptions = gtk.FILL)
+
+class ConditionTable(gtk.Table):
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 5, 3)
+
+        self.attach(gtk.Label('name'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.name_entry = gtk.Entry()
+        self.attach(self.name_entry, 1, 2, 0, 1, yoptions = gtk.FILL)
+        self.name_check = gtk.CheckButton()
+        self.attach(self.name_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+
+        self.attach(gtk.Label('value'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.value_entry = gtk.Entry()
+        self.attach(self.value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.value_check = gtk.CheckButton()
+        self.attach(self.value_check, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
+
+        def add_options(combo):
+            combo.append_text('yes')
+            combo.append_text('no')
+            combo.append_text('empty')
+
+        self.attach(gtk.Label('regex'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.regex_combo = gtk.combo_box_new_text()
+        add_options(self.regex_combo)
+        self.attach(self.regex_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('reverse'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.reverse_combo = gtk.combo_box_new_text()
+        add_options(self.reverse_combo)
+        self.attach(self.reverse_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+
+        def add_sub_element(button, combo, tree):
+            tag = combo.get_model()[combo.get_active()][0]
+            tree.add_sub_element(tag)
+        self.add_sub_element_button = gtk.Button('Add sub-element')
+        self.attach(self.add_sub_element_button, 0, 1, 4, 5, gtk.FILL, 
gtk.FILL)
+        self.add_sub_element_combo = gtk.combo_box_new_text()
+        self.add_sub_element_combo.append_text('USER')
+        self.add_sub_element_combo.append_text('CONDITION')
+        self.add_sub_element_combo.append_text('PERMISSION')
+        self.add_sub_element_combo.append_text('RETURN')
+        self.add_sub_element_combo.append_text('DEFINE')
+        self.add_sub_element_combo.set_active(0)
+        self.attach(self.add_sub_element_combo, 1, 2, 4, 5, gtk.FILL, gtk.FILL)
+        self.add_sub_element_button.connect('clicked', add_sub_element,
+                                            self.add_sub_element_combo,
+                                            security_tree)
+
+class PermissionTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 5, 2)
+
+        def add_options(combo):
+            combo.append_text('YES')
+            combo.append_text('NO')
+            combo.append_text('empty')
+
+        self.attach(gtk.Label('READ'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.read_combo = gtk.combo_box_new_text()
+        add_options(self.read_combo)
+        self.attach(self.read_combo, 1, 2, 0, 1, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('EXECUTE'), 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.execute_combo = gtk.combo_box_new_text()
+        add_options(self.execute_combo)
+        self.attach(self.execute_combo, 1, 2, 1, 2, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('BROWSE'), 0, 1, 2, 3, gtk.FILL, gtk.FILL)
+        self.browse_combo = gtk.combo_box_new_text()
+        add_options(self.browse_combo)
+        self.attach(self.browse_combo, 1, 2, 2, 3, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('DELETE'), 0, 1, 3, 4, gtk.FILL, gtk.FILL)
+        self.delete_combo = gtk.combo_box_new_text()
+        add_options(self.delete_combo)
+        self.attach(self.delete_combo, 1, 2, 3, 4, yoptions = gtk.FILL)
+
+        self.attach(gtk.Label('WRITE'), 0, 1, 4, 5, gtk.FILL, gtk.FILL)
+        self.write_combo = gtk.combo_box_new_text()
+        add_options(self.write_combo)
+        self.attach(self.write_combo, 1, 2, 4, 5, yoptions = gtk.FILL)
+
+class ReturnTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 1, 2)
+
+        self.attach(gtk.Label('value'), 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.value_combo = gtk.combo_box_new_text()
+        self.value_combo.append_text('ALLOW')
+        self.value_combo.append_text('DENY')
+        self.value_combo.append_text('empty')
+        self.attach(self.value_combo, 1, 2, 0, 1, yoptions = gtk.FILL)
+
+class DefinitionTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 1, 2)
+
+class EmptyTable(gtk.Table):
+    def __init__(self, security_tree):
+        gtk.Table.__init__(self, 2, 2)
+
+        self.attach(gtk.Label('Use security file'), 0, 1, 0, 1, gtk.FILL, 
gtk.FILL)
+        self.security_check = gtk.CheckButton()
+        self.attach(self.security_check, 1, 2, 0, 1, yoptions = gtk.FILL)
+        
+        def add_sub_element(button, combo, tree):
+            tag = combo.get_model()[combo.get_active()][0]
+            tree.add_sub_element(tag)
+        self.add_sub_element_button = gtk.Button('Add sub-element')
+        self.attach(self.add_sub_element_button, 0, 1, 1, 2, gtk.FILL, 
gtk.FILL)
+        self.add_sub_element_combo = gtk.combo_box_new_text()
+        self.add_sub_element_combo.append_text('USER')
+        self.add_sub_element_combo.append_text('CONDITION')
+        self.add_sub_element_combo.append_text('PERMISSION')
+        self.add_sub_element_combo.append_text('RETURN')
+        self.add_sub_element_combo.append_text('DEFINE')
+        self.add_sub_element_combo.set_active(0)
+        self.attach(self.add_sub_element_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
+        self.add_sub_element_button.connect('clicked', add_sub_element,
+                                            self.add_sub_element_combo,
+                                            security_tree)
+
+class SecurityTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 1, 2)
+
+        security_tree = SecurityTree(self)
+
+        self.user_table = UserTable()
+        self.condition_table = ConditionTable(security_tree)
+        self.permission_table = PermissionTable()
+        self.return_table = ReturnTable()
+        self.definition_table = DefinitionTable()
+        self.empty_table = EmptyTable(security_tree)
+        self.current_table = self.empty_table
+
+        self.attach(security_tree.scroll, 0, 1, 0, 1)
+        self.attach(self.current_table, 1, 2, 0, 1, gtk.FILL)
+
+    def switch_table(self, tag): # switch doesn't work
+        self.remove(self.current_table)
+        if tag == 'USER':
+            self.current_table = self.user_table
+        elif tag == 'CONDITION':
+            self.current_table = self.condition_table
+        elif tag == 'PERMISSION':
+            self.current_table = self.permission_table
+        elif tag == 'RETURN':
+            self.current_table = self.return_table
+        elif tag == 'DEFINITION':
+            self.current_table = self.definition_table
+        elif tag == 'SECURITY':
+            self.current_table = self.empty_table
+        self.attach(self.current_table, 1, 2, 0, 1, gtk.FILL)



commit 1020bab81ba10ebfd055d26e47276874826b4486
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Aug 16 18:27:18 2009 +0200

    Complete tests for pycontrollib security classes.

diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index d965915..aefaf57 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -410,14 +410,22 @@ class Return(SecurityElement):
 
 class SecurityList():
     def __init__(self, elements = []):
-        for elements in elements:
+        self.elements = []
+        for element in elements:
             self.add_element(element)
         self.custom = []
         self.custom_attrib = {}
 
+    def __eq__(self, other):
+        return isinstance(other, SecurityList) and \
+            self.elements == other.elements
+
     def get_elements(self):
         return self.elements
 
+    def get_element(self, index):
+        return self.elements[index]
+
     def add_element(self, element, index = None):
         if index is None:
             self.elements.append(element)
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
index 218c3d7..a9e4ba3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
@@ -867,6 +867,163 @@ class UserTest(unittest.TestCase):
         self.assertRaises(AttributeError, User.from_lxml_element,
                           etree.XML(text))
 
+class ReturnTest(unittest.TestCase):
+    def test_creation(self):
+        ret = Return()
+        ret = Return('ALLOW')
+
+    def test_value(self):
+        ret = Return('ALLOW')
+        self.assertEqual('ALLOW', ret.get_value())
+        ret.set_value('DENY')
+        self.assertEqual('DENY', ret.get_value())
+        ret.set_value(None)
+        self.assertEqual(None, ret.get_value())
+        ret = Return(value = 'ALLOW')
+        self.assertEqual('ALLOW', ret.get_value())
+
+    def test_equality(self):
+        self.assertEqual(Return('ALLOW'), Return('ALLOW'))
+        self.assertNotEqual(Return('ALLOW'), Return('DENY'))
+        self.assertNotEqual(Return(), 'another type')
+
+    def test_from_string(self):
+        text = '<RETURN />'
+        ret = Return.from_string(text)
+        right = Return()
+        self.assertEqual(ret, right)
+
+    def test_from_string_value(self):
+        text = '<RETURN value="ALLOW" />'
+        ret = Return.from_string(text)
+        right = Return(value = 'ALLOW')
+        self.assertEqual(ret, right)
+
+    def test_from_lxml(self):
+        text = '<RETURN />'
+        ret = Return.from_lxml_element(etree.XML(text))
+        right = Return()
+        self.assertEqual(ret, right)
+
+    def test_from_lxml_value(self):
+        text = '<RETURN value="ALLOW" />'
+        ret = Return.from_lxml_element(etree.XML(text))
+        right = Return(value = 'ALLOW')
+        self.assertEqual(ret, right)
+
+    def test_to_string(self):
+        ret = Return()
+        copy = Return.from_string(str(ret))
+        self.assertEqual(ret, copy)
+
+    def test_to_string_value(self):
+        ret = Return(value = 'ALLOW')
+        copy = Return.from_string(str(ret))
+        self.assertEqual(ret, copy)
+
+    def test_to_lxml(self):
+        ret = Return()
+        copy = Return.from_lxml_element(ret.to_lxml_element())
+        self.assertEqual(ret, copy)
+
+    def test_to_lxml_value(self):
+        ret = Return(value = 'ALLOW')
+        copy = Return.from_lxml_element(ret.to_lxml_element())
+        self.assertEqual(ret, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, Return.from_string, text)
+        self.assertRaises(AttributeError, Return.from_lxml_element,
+                          etree.XML(text))
+
+class SecurityListTest(unittest.TestCase):
+    def test_creation(self):
+        slist = SecurityList()
+        slist = SecurityList([Permission(), Return()])
+
+    def test_elements(self):
+        element_0 = Condition()
+        element_1 = Return()
+        slist = SecurityList()
+        self.assertEqual(0, len(slist.get_elements()))
+        slist.add_element(element_0)
+        self.assertEqual(1, len(slist.get_elements()))
+        self.assertEqual(element_0, slist.get_element(0))
+        slist.add_element(element_1)
+        self.assertEqual(2, len(slist.get_elements()))
+        self.assertEqual(element_0, slist.get_element(0))
+        self.assertEqual(element_1, slist.get_element(1))
+        slist.remove_element(0)
+        self.assertEqual(1, len(slist.get_elements()))
+        self.assertEqual(element_1, slist.get_element(0))
+        slist.add_element(element_0, 0)
+        self.assertEqual(2, len(slist.get_elements()))
+        self.assertEqual(element_0, slist.get_element(0))
+        self.assertEqual(element_1, slist.get_element(1))
+        self.assertRaises(IndexError, slist.get_element, 2)
+        slist = SecurityList(elements = [element_0, element_1])
+        self.assertEqual(2, len(slist.get_elements()))
+        self.assertEqual(element_0, slist.get_element(0))
+        self.assertEqual(element_1, slist.get_element(1))
+
+    def test_equality(self):
+        self.assertEqual(SecurityList([Condition()]),
+                         SecurityList([Condition()]))
+        self.assertNotEqual(SecurityList([Condition()]),
+                            SecurityList([]))
+        self.assertNotEqual(SecurityList(), 'another type')
+
+    def test_from_string(self):
+        text = '<SECURITY />'
+        slist = SecurityList.from_string(text)
+        right = SecurityList()
+        self.assertEqual(slist, right)
+
+    def test_from_string_elements(self):
+        text = '<SECURITY><CONDITION /><RETURN /></SECURITY>'
+        slist = SecurityList.from_string(text)
+        right = SecurityList([Condition(), Return()])
+        self.assertEqual(slist, right)
+
+    def test_from_lxml(self):
+        text = '<SECURITY />'
+        slist = SecurityList.from_lxml_element(etree.XML(text))
+        right = SecurityList()
+        self.assertEqual(slist, right)
+
+    def test_from_lxml_elements(self):
+        text = '<SECURITY><CONDITION /><RETURN /></SECURITY>'
+        slist = SecurityList.from_lxml_element(etree.XML(text))
+        right = SecurityList([Condition(), Return()])
+        self.assertEqual(slist, right)
+
+    def test_to_string(self):
+        slist = SecurityList()
+        copy = SecurityList.from_string(str(slist))
+        self.assertEqual(slist, copy)
+
+    def test_to_string_elements(self):
+        slist = SecurityList([Condition(), Return()])
+        copy = SecurityList.from_string(str(slist))
+        self.assertEqual(slist, copy)
+
+    def test_to_lxml(self):
+        slist = SecurityList()
+        copy = SecurityList.from_lxml_element(slist.to_lxml_element())
+        self.assertEqual(slist, copy)
+
+    def test_to_string_elements(self):
+        slist = SecurityList([Condition(), Return()])
+        copy = SecurityList.from_lxml_element(slist.to_lxml_element())
+        self.assertEqual(slist, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, SecurityList.from_string, text)
+        self.assertRaises(AttributeError, SecurityList.from_lxml_element,
+                          etree.XML(text))
+
 class BadMarkupTest(unittest.TestCase):
     def condition_test(self):
         text = '''
@@ -923,5 +1080,43 @@ class BadMarkupTest(unittest.TestCase):
         custom = custom[0]
         self.assertEqual('CUSTOM', custom.tag)
 
+    def return_test(self):
+        text = '''
+<RETURN custom="unknown">
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</RETURN>'''
+        ret = Return.from_string(text)
+        tree = ret.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
+    def security_list_test(self):
+        text = '''
+<SECURITY custom="unknown">
+  <CONDITION />
+  <RETURN />
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</SECURITY>'''
+        slist = SecurityList.from_string(text)
+        tree = slist.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()



commit b4f58841cb0d02634db8f3b85dc86583e6ff44e4
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Aug 16 17:49:05 2009 +0200

    Pycontrollib User tests.

diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index f420b62..d965915 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -259,9 +259,15 @@ class User(SecurityElement):
         self.set_browse(browse)
         self.set_delete(delete)
         self.set_write(write)
-        self.custom_attributes = {}
+        self.custom_attrib = {}
         self.custom = []
 
+    def __eq__(self, other):
+        return isinstance(other, User) and self.name == other.name and \
+            self.password == other.password and self.read == other.read and \
+            self.execute == other.execute and self.browse == other.browse and \
+            self.delete == other.delete and self.write == other.write
+
     def set_name(self, name):
         self.name = name
 
@@ -305,21 +311,21 @@ class User(SecurityElement):
         return self.write
 
     def to_lxml_element(self):
-        root = etree.Element('PERMISSION')
+        root = etree.Element('USER')
         if self.name is not None:
             root.set('name', self.name)
         if self.password is not None:
             root.set('password', self.password)
         if self.read is not None:
-            root.set('READ', self.read)
+            root.set('READ', 'YES' if self.read else 'NO')
         if self.execute is not None:
-            root.set('EXECUTE', self.execute)
+            root.set('EXECUTE', 'YES' if self.execute else 'NO')
         if self.browse is not None:
-            root.set('BROWSE', self.browse)
+            root.set('BROWSE', 'YES' if self.browse else 'NO')
         if self.delete is not None:
-            root.set('DELETE', self.delete)
+            root.set('DELETE', 'YES' if self.delete else 'NO')
         if self.write is not None:
-            root.set('WRITE', self.write)
+            root.set('WRITE', 'YES' if self.write else 'NO')
         for key, value in self.custom_attrib.iteritems():
             root.set(key, value)
         for element in self.custom:
@@ -334,10 +340,20 @@ class User(SecurityElement):
         name = custom_attrib.pop('name', None)
         password = custom_attrib.pop('password', None)
         read = custom_attrib.pop('READ', None)
+        if read is not None:
+            read = read.upper() == 'YES'
         execute = custom_attrib.pop('EXECUTE', None)
+        if execute is not None:
+            execute = execute.upper() == 'YES'
         browse = custom_attrib.pop('BROWSE', None)
+        if browse is not None:
+            browse = browse.upper() == 'YES'
         delete = custom_attrib.pop('DELETE', None)
+        if delete is not None:
+            delete = delete.upper() == 'YES'
         write = custom_attrib.pop('WRITE', None)
+        if write is not None:
+            write = write.upper() == 'YES'
         custom = list(root)
         user = User(name, password, read, execute, browse,
                     delete, write)
@@ -348,7 +364,7 @@ class User(SecurityElement):
     @staticmethod
     def from_string(text):
         '''Factory to produce user element by parsing a string.'''
-        return Permission.from_lxml_element(etree.XML(text))
+        return User.from_lxml_element(etree.XML(text))
 
 class Return(SecurityElement):
     def __init__(self, value = None):
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
index d8d819d..218c3d7 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
@@ -551,6 +551,322 @@ class PermissionTest(unittest.TestCase):
         self.assertRaises(AttributeError, Permission.from_lxml_element,
                           etree.XML(text))
 
+class UserTest(unittest.TestCase):
+    def test_creation(self):
+        user = User()
+        user = User('name')
+        user = User('name', 'password')
+        user = User('name', 'password', True)
+        user = User('name', 'password', True, True)
+        user = User('name', 'password', True, True, True)
+        user = User('name', 'password', True, True, True, True)
+        user = User('name', 'password', True, True, True, True, True)
+
+    def test_name(self):
+        user = User('name')
+        self.assertEqual('name', user.get_name())
+        user.set_name('other')
+        self.assertEqual('other', user.get_name())
+        user.set_name(None)
+        self.assertEqual(None, user.get_name())
+        user = User(name = 'name')
+        self.assertEqual('name', user.get_name())
+
+    def test_password(self):
+        user = User('name', 'password')
+        self.assertEqual('password', user.get_password())
+        user.set_password('other')
+        self.assertEqual('other', user.get_password())
+        user.set_password(None)
+        self.assertEqual(None, user.get_password())
+        user = User(password = 'password')
+        self.assertEqual('password', user.get_password())
+
+    def test_read(self):
+        user = User('name', 'password', True)
+        self.assertTrue(user.get_read())
+        user.set_read(False)
+        self.assertFalse(user.get_read())
+        user.set_read(None)
+        self.assertEqual(None, user.get_read())
+        user = User(read = True)
+        self.assertTrue(user.get_read())
+
+    def test_execute(self):
+        user = User('name', 'password', True, True)
+        self.assertTrue(user.get_execute())
+        user.set_execute(False)
+        self.assertFalse(user.get_execute())
+        user.set_execute(None)
+        self.assertEqual(None, user.get_execute())
+        user = User(execute = True)
+        self.assertTrue(user.get_execute())
+
+    def test_browse(self):
+        user = User('name', 'password', True, True, False)
+        self.assertFalse(user.get_browse())
+        user.set_browse(True)
+        self.assertTrue(user.get_browse())
+        user.set_browse(None)
+        self.assertEqual(None, user.get_browse())
+        user = User(browse = True)
+        self.assertTrue(user.get_browse())
+
+    def test_delete(self):
+        user = User('name', 'password', True, True, False, False)
+        self.assertFalse(user.get_delete())
+        user.set_delete(True)
+        self.assertTrue(user.get_delete())
+        user.set_delete(None)
+        self.assertEqual(None, user.get_delete())
+        user = User(delete = True)
+        self.assertTrue(user.get_delete())
+
+    def test_write(self):
+        user = User('name', 'password', True, True, False, False, False)
+        self.assertFalse(user.get_write())
+        user.set_write(True)
+        self.assertTrue(user.get_write())
+        user.set_write(None)
+        self.assertEqual(None, user.get_write())
+        user = User(write = True)
+        self.assertTrue(user.get_write())
+
+    def test_equality(self):
+        self.assertEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', False, False, False, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('other', 'password', False, False, False, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'other', False, False, False, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', True, False, False, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', False, True, False, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', False, False, True, False, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', False, False, False, True, False))
+        self.assertNotEqual(
+            User('name', 'password', False, False, False, False, False),
+            User('name', 'password', False, False, False, False, True))
+        self.assertNotEqual(User(), 'another type')
+
+    def test_from_string(self):
+        text = '<USER />'
+        user = User.from_string(text)
+        right = User()
+        self.assertEqual(user, right)
+
+    def test_from_string_name(self):
+        text = '<USER name="name" />'
+        user = User.from_string(text)
+        right = User(name = 'name')
+        self.assertEqual(user, right)
+
+    def test_from_string_password(self):
+        text = '<USER password="pass" />'
+        user = User.from_string(text)
+        right = User(password = 'pass')
+        self.assertEqual(user, right)
+
+    def test_from_string_read(self):
+        text = '<USER READ="NO" />'
+        user = User.from_string(text)
+        right = User(read = False)
+        self.assertEqual(user, right)
+
+    def test_from_string_execute(self):
+        text = '<USER EXECUTE="NO" />'
+        user = User.from_string(text)
+        right = User(execute = False)
+        self.assertEqual(user, right)
+
+    def test_from_string_browse(self):
+        text = '<USER BROWSE="NO" />'
+        user = User.from_string(text)
+        right = User(browse = False)
+        self.assertEqual(user, right)
+
+    def test_from_string_delete(self):
+        text = '<USER DELETE="NO" />'
+        user = User.from_string(text)
+        right = User(delete = False)
+        self.assertEqual(user, right)
+
+    def test_from_string_write(self):
+        text = '<USER WRITE="NO" />'
+        user = User.from_string(text)
+        right = User(write = False)
+        self.assertEqual(user, right)
+
+    def test_from_string_full(self):
+        text = '''
+<USER name="name" password="pass" READ="NO" EXECUTE="YES" BROWSE="NO"
+      DELETE="YES" WRITE="NO" />'''
+        user = User.from_string(text)
+        right = User('name', 'pass', False, True, False, True, False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml(self):
+        text = '<USER />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User()
+        self.assertEqual(user, right)
+
+    def test_from_lxml_name(self):
+        text = '<USER name="name" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(name = 'name')
+        self.assertEqual(user, right)
+
+    def test_from_lxml_password(self):
+        text = '<USER password="pass" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(password = 'pass')
+        self.assertEqual(user, right)
+
+    def test_from_lxml_read(self):
+        text = '<USER READ="NO" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(read = False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml_execute(self):
+        text = '<USER EXECUTE="NO" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(execute = False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml_browse(self):
+        text = '<USER BROWSE="NO" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(browse = False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml_delete(self):
+        text = '<USER DELETE="NO" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(delete = False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml_write(self):
+        text = '<USER WRITE="NO" />'
+        user = User.from_lxml_element(etree.XML(text))
+        right = User(write = False)
+        self.assertEqual(user, right)
+
+    def test_from_lxml_full(self):
+        text = '''
+<USER name="name" password="pass" READ="NO" EXECUTE="YES" BROWSE="NO"
+      DELETE="YES" WRITE="NO" />'''
+        user = User.from_lxml_element(etree.XML(text))
+        right = User('name', 'pass', False, True, False, True, False)
+        self.assertEqual(user, right)
+
+    def test_to_string(self):
+        user = User()
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_name(self):
+        user = User(name = 'name')
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_password(self):
+        user = User(password = 'pass')
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_read(self):
+        user = User(read = True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_execute(self):
+        user = User(execute = True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_browse(self):
+        user = User(browse = True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_delete(self):
+        user = User(delete = True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_write(self):
+        user = User(write = True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_string_full(self):
+        user = User('name', 'pass', True, False, True, False, True)
+        copy = User.from_string(str(user))
+        self.assertEqual(user, copy)
+
+    def test_to_lxml(self):
+        user = User()
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_name(self):
+        user = User(name = 'name')
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_password(self):
+        user = User(password = 'pass')
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_read(self):
+        user = User(read = True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_execute(self):
+        user = User(execute = True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_browse(self):
+        user = User(browse = True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_delete(self):
+        user = User(delete = True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_write(self):
+        user = User(write = True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_to_lxml_full(self):
+        user = User('name', 'pass', True, False, True, False, True)
+        copy = User.from_lxml_element(user.to_lxml_element())
+        self.assertEqual(user, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, User.from_string, text)
+        self.assertRaises(AttributeError, User.from_lxml_element,
+                          etree.XML(text))
+
 class BadMarkupTest(unittest.TestCase):
     def condition_test(self):
         text = '''
@@ -589,5 +905,23 @@ class BadMarkupTest(unittest.TestCase):
         custom = custom[0]
         self.assertEqual('CUSTOM', custom.tag)
 
+    def user_test(self):
+        text = '''
+<USER custom="unknown">
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</USER>'''
+        user = User.from_string(text)
+        tree = user.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()



commit a809ac0d448bf74cc4e454fed0d17ce7707c41fb
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Aug 16 16:33:02 2009 +0200

    Pycontrollib Permission tests.

diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index faa7fd3..f420b62 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -203,15 +203,15 @@ class Permission(SecurityElement):
     def to_lxml_element(self):
         root = etree.Element('PERMISSION')
         if self.read is not None:
-            root.set('READ', self.read)
+            root.set('READ', 'YES' if self.read else 'NO')
         if self.execute is not None:
-            root.set('EXECUTE', self.execute)
+            root.set('EXECUTE', 'YES' if self.execute else 'NO')
         if self.browse is not None:
-            root.set('BROWSE', self.browse)
+            root.set('BROWSE', 'YES' if self.browse else 'NO')
         if self.delete is not None:
-            root.set('DELETE', self.delete)
+            root.set('DELETE', 'YES' if self.delete else 'NO')
         if self.write is not None:
-            root.set('WRITE', self.write)
+            root.set('WRITE', 'YES' if self.write else 'NO')
         for key, value in self.custom_attrib.iteritems():
             root.set(key, value)
         for element in self.custom:
@@ -224,10 +224,20 @@ class Permission(SecurityElement):
             raise AttributeError('Expected PERMISSION tag.')
         custom_attrib = root.attrib
         read = custom_attrib.pop('READ', None)
+        if read is not None:
+            read = read.upper() == 'YES'
         execute = custom_attrib.pop('EXECUTE', None)
+        if execute is not None:
+            execute = execute.upper() == 'YES'
         browse = custom_attrib.pop('BROWSE', None)
+        if browse is not None:
+            browse = browse.upper() == 'YES'
         delete = custom_attrib.pop('DELETE', None)
+        if delete is not None:
+            delete = delete.upper() == 'YES'
         write = custom_attrib.pop('WRITE', None)
+        if write is not None:
+            write = write.upper() == 'YES'
         custom = list(root)
         permission = Permission(read, execute, browse, delete, write)
         permission.custom_attrib = custom_attrib
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
index 214bfc6..d8d819d 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
@@ -309,6 +309,248 @@ class ConditionTest(unittest.TestCase):
         self.assertRaises(AttributeError, Condition.from_lxml_element,
                           etree.XML(text))
 
+class PermissionTest(unittest.TestCase):
+    def test_creation(self):
+        permission = Permission()
+        permission = Permission(True)
+        permission = Permission(True, True)
+        permission = Permission(True, True, True)
+        permission = Permission(True, True, True, True)
+        permission = Permission(True, True, True, True, True)
+
+    def test_read(self):
+        permission = Permission(True)
+        self.assertTrue(permission.get_read())
+        permission.set_read(False)
+        self.assertFalse(permission.get_read())
+        permission.set_read(None)
+        self.assertEqual(None, permission.get_read())
+        permission = Permission(read = True)
+        self.assertTrue(permission.get_read())
+
+    def test_execute(self):
+        permission = Permission(True, True)
+        self.assertTrue(permission.get_execute())
+        permission.set_execute(False)
+        self.assertFalse(permission.get_execute())
+        permission.set_execute(None)
+        self.assertEqual(None, permission.get_execute())
+        permission = Permission(execute = True)
+        self.assertTrue(permission.get_execute())
+
+    def test_browse(self):
+        permission = Permission(True, True, False)
+        self.assertFalse(permission.get_browse())
+        permission.set_browse(True)
+        self.assertTrue(permission.get_browse())
+        permission.set_browse(None)
+        self.assertEqual(None, permission.get_browse())
+        permission = Permission(browse = True)
+        self.assertTrue(permission.get_browse())
+
+    def test_delete(self):
+        permission = Permission(True, True, False, False)
+        self.assertFalse(permission.get_delete())
+        permission.set_delete(True)
+        self.assertTrue(permission.get_delete())
+        permission.set_delete(None)
+        self.assertEqual(None, permission.get_delete())
+        permission = Permission(delete = True)
+        self.assertTrue(permission.get_delete())
+
+    def test_write(self):
+        permission = Permission(True, True, False, False, False)
+        self.assertFalse(permission.get_write())
+        permission.set_write(True)
+        self.assertTrue(permission.get_write())
+        permission.set_write(None)
+        self.assertEqual(None, permission.get_write())
+        permission = Permission(write = True)
+        self.assertTrue(permission.get_write())
+
+    def test_equality(self):
+        self.assertEqual(
+            Permission(False, False, False, False, False),
+            Permission(False, False, False, False, False))
+        self.assertNotEqual(
+            Permission(False, False, False, False, False),
+            Permission(True, False, False, False, False))
+        self.assertNotEqual(
+            Permission(False, False, False, False, False),
+            Permission(False, True, False, False, False))
+        self.assertNotEqual(
+            Permission(False, False, False, False, False),
+            Permission(False, False, True, False, False))
+        self.assertNotEqual(
+            Permission(False, False, False, False, False),
+            Permission(False, False, False, True, False))
+        self.assertNotEqual(
+            Permission(False, False, False, False, False),
+            Permission(False, False, False, False, True))
+        self.assertNotEqual(Permission(), 'another type')
+
+    def test_from_string(self):
+        text = '<PERMISSION />'
+        permission = Permission.from_string(text)
+        right = Permission()
+        self.assertEqual(permission, right)
+
+    def test_from_string_read(self):
+        text = '<PERMISSION READ="NO" />'
+        permission = Permission.from_string(text)
+        right = Permission(read = False)
+        self.assertEqual(permission, right)
+
+    def test_from_string_execute(self):
+        text = '<PERMISSION EXECUTE="NO" />'
+        permission = Permission.from_string(text)
+        right = Permission(execute = False)
+        self.assertEqual(permission, right)
+
+    def test_from_string_browse(self):
+        text = '<PERMISSION BROWSE="NO" />'
+        permission = Permission.from_string(text)
+        right = Permission(browse = False)
+        self.assertEqual(permission, right)
+
+    def test_from_string_delete(self):
+        text = '<PERMISSION DELETE="NO" />'
+        permission = Permission.from_string(text)
+        right = Permission(delete = False)
+        self.assertEqual(permission, right)
+
+    def test_from_string_write(self):
+        text = '<PERMISSION WRITE="NO" />'
+        permission = Permission.from_string(text)
+        right = Permission(write = False)
+        self.assertEqual(permission, right)
+
+    def test_from_string_full(self):
+        text = '''
+<PERMISSION READ="NO" EXECUTE="YES" BROWSE="NO" DELETE="YES" WRITE="NO" />'''
+        permission = Permission.from_string(text)
+        right = Permission(False, True, False, True, False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml(self):
+        text = '<PERMISSION />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission()
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_read(self):
+        text = '<PERMISSION READ="NO" />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(read = False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_execute(self):
+        text = '<PERMISSION EXECUTE="NO" />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(execute = False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_browse(self):
+        text = '<PERMISSION BROWSE="NO" />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(browse = False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_delete(self):
+        text = '<PERMISSION DELETE="NO" />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(delete = False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_write(self):
+        text = '<PERMISSION WRITE="NO" />'
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(write = False)
+        self.assertEqual(permission, right)
+
+    def test_from_lxml_full(self):
+        text = '''
+<PERMISSION READ="NO" EXECUTE="YES" BROWSE="NO" DELETE="YES" WRITE="NO" />'''
+        permission = Permission.from_lxml_element(etree.XML(text))
+        right = Permission(False, True, False, True, False)
+        self.assertEqual(permission, right)
+
+    def test_to_string(self):
+        permission = Permission()
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_read(self):
+        permission = Permission(read = True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_execute(self):
+        permission = Permission(execute = True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_browse(self):
+        permission = Permission(browse = True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_delete(self):
+        permission = Permission(delete = True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_write(self):
+        permission = Permission(write = True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_string_full(self):
+        permission = Permission(True, False, True, False, True)
+        copy = Permission.from_string(str(permission))
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml(self):
+        permission = Permission()
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_read(self):
+        permission = Permission(read = True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_execute(self):
+        permission = Permission(execute = True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_browse(self):
+        permission = Permission(browse = True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_delete(self):
+        permission = Permission(delete = True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_write(self):
+        permission = Permission(write = True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_to_lxml_full(self):
+        permission = Permission(True, False, True, False, True)
+        copy = Permission.from_lxml_element(permission.to_lxml_element())
+        self.assertEqual(permission, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, Permission.from_string, text)
+        self.assertRaises(AttributeError, Permission.from_lxml_element,
+                          etree.XML(text))
+
 class BadMarkupTest(unittest.TestCase):
     def condition_test(self):
         text = '''
@@ -329,5 +571,23 @@ class BadMarkupTest(unittest.TestCase):
         custom = custom[0]
         self.assertEqual('CUSTOM', custom.tag)
 
+    def permission_test(self):
+        text = '''
+<PERMISSION custom="unknown">
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</CONDITION>'''
+        permission = Permission.from_string(text)
+        tree = permission.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()



commit 4d8bcd32cc36b1ffc31120e0208b96081b52795f
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Aug 16 13:36:27 2009 +0200

    Pycontrollib Condition tests.

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index bce43b9..b8e5b03 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -134,9 +134,9 @@ class DefinitionTree(Definition):
         attributes. values is expected to be iterable.'''
         Definition.__init__(self, name, attributes)
         self.definitions = []
+        self.custom = [] # list of children not being definitions
         for definition in definitions:
             self.add_definition(definition)
-        self.custom = [] # list of children not being definitions
 
     def __eq__(self, other):
         return isinstance(other, DefinitionTree) and \
diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index 1cd1141..faa7fd3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -22,6 +22,8 @@ from definition import Definition
 class SecurityElement():
     @staticmethod
     def from_lxml_element(root):
+        if root.tag == 'USER':
+            return User.from_lxml_element(root)
         if root.tag == 'CONDITION':
             return Condition.from_lxml_element(root)
         elif root.tag == 'PERMISSION':
@@ -49,19 +51,25 @@ class Condition(SecurityElement):
         self.set_value(value)
         self.set_reverse(reverse)
         self.set_regex(regex)
-        self.custom_attributes = {}
+        self.custom_attrib = {}
         self.custom = []
         self.sub_elements = []
         for element in sub_elements:
             self.add_sub_element(element)
 
+    def __eq__(self, other):
+        return isinstance(other, Condition) and self.name == other.name and \
+            self.value == other.value and self.reverse == other.reverse and \
+            self.regex == other.regex and \
+            self.sub_elements == other.sub_elements
+
     def set_name(self, name):
         self.name = name
 
     def get_name(self):
         return self.name
 
-    def set_value(value):
+    def set_value(self, value):
         self.value = value
 
     def get_value(self):
@@ -73,16 +81,19 @@ class Condition(SecurityElement):
     def get_reverse(self):
         return self.reverse
 
-    def set_regex(self):
+    def set_regex(self, regex):
         self.regex = regex
 
     def get_regex(self):
         return self.regex
 
-    def add_sub_element(self, element):
+    def add_sub_element(self, element, index = None):
         if isinstance(element, Definition) or \
                 isinstance(element, SecurityElement):
-            self.sub_elements.append(element)
+            if index is None:
+                self.sub_elements.append(element)
+            else:
+                self.sub_elements.insert(index, element)
         else:
             self.custom.append(element)
 
@@ -92,6 +103,9 @@ class Condition(SecurityElement):
     def get_sub_element(self, index):
         return self.sub_elements[index]
 
+    def remove_sub_element(self, index):
+        self.sub_elements.pop(index)
+
     def to_lxml_element(self):
         root = etree.Element('CONDITION')
         if self.name is not None:
@@ -101,7 +115,7 @@ class Condition(SecurityElement):
         if self.reverse is not None:
             root.set('not', 'yes' if self.reverse else 'no')
         if self.regex is not None:
-            root.set('regex', 'yes' if self.reverse else 'no')
+            root.set('regex', 'yes' if self.regex else 'no')
         for key, value in self.custom_attrib.iteritems():
             root.set(key, value)
         for element in self.sub_elements:
@@ -118,7 +132,11 @@ class Condition(SecurityElement):
         name = custom_attrib.pop('name', None)
         value = custom_attrib.pop('value', None)
         reverse = custom_attrib.pop('not', None)
+        if reverse is not None:
+            reverse = reverse.upper() == 'YES'
         regex = custom_attrib.pop('regex', None)
+        if regex is not None:
+            regex = regex.upper() == 'YES'
         custom = []
         sub_elements = []
         for child in list(root):
@@ -144,9 +162,14 @@ class Permission(SecurityElement):
         self.set_browse(browse)
         self.set_delete(delete)
         self.set_write(write)
-        self.custom_attributes = {}
+        self.custom_attrib = {}
         self.custom = []
 
+    def __eq__(self, other):
+        return isinstance(other, Permission) and self.read == other.read and \
+            self.execute == other.execute and self.browse == other.browse and \
+            self.delete == other.delete and self.write == other.write
+
     def set_read(self, read):
         self.read = read
 
@@ -306,11 +329,11 @@ class User(SecurityElement):
         delete = custom_attrib.pop('DELETE', None)
         write = custom_attrib.pop('WRITE', None)
         custom = list(root)
-        permission = Permission(name, password, read, execute, browse,
-                                delete, write)
-        permission.custom_attrib = custom_attrib
-        permission.custom = custom
-        return permission
+        user = User(name, password, read, execute, browse,
+                    delete, write)
+        user.custom_attrib = custom_attrib
+        user.custom = custom
+        return user
 
     @staticmethod
     def from_string(text):
@@ -320,9 +343,12 @@ class User(SecurityElement):
 class Return(SecurityElement):
     def __init__(self, value = None):
         self.set_value(value)
-        self.custom_attributes = {}
+        self.custom_attrib = {}
         self.custom = []
 
+    def __eq__(self, other):
+        return isinstance(other, Return) and self.value == other.value
+
     def set_value(self, value):
         self.value = value
 
@@ -384,7 +410,7 @@ class SecurityList():
         for element in list(root):
             try:
                 elements.append(SecurityElement.from_lxml_element(element))
-            else:
+            except:
                 custom.append(element)
         security_list = SecurityList(elements)
         security_list.custom = custom
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
index 5a784d4..7c7179e 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/Makefile
+++ b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
@@ -16,9 +16,11 @@
 PYTHON=/usr/bin/python
 PYTHONPATH:=$(shell dirname `pwd`):${PYTHONPATH}
 
-.PHONY: test log_test definition_test mimetypes_test vhost_test config_test
+.PHONY: test log_test definition_test mimetypes_test vhost_test config_test \
+       security_test
 
-test: log_test definition_test mimetypes_test vhost_test config_test
+test: log_test definition_test mimetypes_test vhost_test config_test \
+       security_test
 
 log_test:
        -PYTHONPATH=${PYTHONPATH} ${PYTHON} log_test.py
@@ -35,6 +37,9 @@ vhost_test:
 config_test:
        -PYTHONPATH=${PYTHONPATH} ${PYTHON} config_test.py
 
+security_test:
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} security_test.py
+
 .PHONY: clean
 clean:
        rm *.py[co]
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/security_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
new file mode 100644
index 0000000..214bfc6
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/test/security_test.py
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from security import SecurityElement, User, Condition, Permission, Return, 
SecurityList
+from definition import Definition
+from lxml import etree
+import unittest
+
+class SecurityElementTest(unittest.TestCase):
+    def test_creation(self):
+        element = SecurityElement()
+
+    def test_from_string(self):
+        text = '<USER />'
+        element = SecurityElement.from_string(text)
+        self.assertTrue(isinstance(element, User))
+        text = '<CONDITION />'
+        element = SecurityElement.from_string(text)
+        self.assertTrue(isinstance(element, Condition))
+        text = '<PERMISSION />'
+        element = SecurityElement.from_string(text)
+        self.assertTrue(isinstance(element, Permission))
+        text = '<RETURN />'
+        element = SecurityElement.from_string(text)
+        self.assertTrue(isinstance(element, Return))
+        text = '<DEFINE />'
+        element = SecurityElement.from_string(text)
+        self.assertTrue(isinstance(element, Definition))
+
+class ConditionTest(unittest.TestCase):
+    def test_creation(self):
+        condition = Condition()
+        condition = Condition('name')
+        condition = Condition('name', 'value')
+        condition = Condition('name', 'value', False)
+        condition = Condition('name', 'value', False, False)
+        condition = Condition('name', 'value', False, False, [])
+
+    def test_name(self):
+        condition = Condition('name1')
+        self.assertEqual('name1', condition.get_name())
+        condition.set_name('name2')
+        self.assertEqual('name2', condition.get_name())
+        condition.set_name(None)
+        self.assertEqual(None, condition.get_name())
+        condition = Condition(name = 'name1')
+        self.assertEqual('name1', condition.get_name())
+
+    def test_value(self):
+        condition = Condition('name1', 'value1')
+        self.assertEqual('value1', condition.get_value())
+        condition.set_value('value2')
+        self.assertEqual('value2', condition.get_value())
+        condition.set_value(None)
+        self.assertEqual(None, condition.get_value())
+        condition = Condition(value = 'value1')
+        self.assertEqual('value1', condition.get_value())
+
+    def test_reverse(self):
+        condition = Condition('name1', 'value1', False)
+        self.assertFalse(condition.get_reverse())
+        condition.set_reverse(True)
+        self.assertTrue(condition.get_reverse())
+        condition.set_reverse(None)
+        self.assertEqual(None, condition.get_reverse())
+        condition = Condition(reverse = True)
+        self.assertTrue(condition.get_reverse())
+
+    def test_regex(self):
+        condition = Condition('name1', 'value1', False, False)
+        self.assertFalse(condition.get_regex())
+        condition.set_regex(True)
+        self.assertTrue(condition.get_regex())
+        condition.set_regex(None)
+        self.assertEqual(None, condition.get_regex())
+        condition = Condition(regex = True)
+        self.assertTrue(condition.get_regex())
+
+    def test_sub_elements(self):
+        element_0 = Condition()
+        element_1 = Return()
+        condition = Condition()
+        self.assertEqual(0, len(condition.get_sub_elements()))
+        condition.add_sub_element(element_0)
+        self.assertEqual(1, len(condition.get_sub_elements()))
+        self.assertEqual(element_0, condition.get_sub_element(0))
+        condition.add_sub_element(element_1)
+        self.assertEqual(2, len(condition.get_sub_elements()))
+        self.assertEqual(element_0, condition.get_sub_element(0))
+        self.assertEqual(element_1, condition.get_sub_element(1))
+        condition.remove_sub_element(0)
+        self.assertEqual(1, len(condition.get_sub_elements()))
+        self.assertEqual(element_1, condition.get_sub_element(0))
+        condition.add_sub_element(element_0, 0)
+        self.assertEqual(2, len(condition.get_sub_elements()))
+        self.assertEqual(element_0, condition.get_sub_element(0))
+        self.assertEqual(element_1, condition.get_sub_element(1))
+        self.assertRaises(IndexError, condition.get_sub_element, 2)
+        condition = Condition(sub_elements = [element_0, element_1])
+        self.assertEqual(2, len(condition.get_sub_elements()))
+        self.assertEqual(element_0, condition.get_sub_element(0))
+        self.assertEqual(element_1, condition.get_sub_element(1))
+
+    def test_equality(self):
+        self.assertEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name1', 'value1', False, False, []))
+        self.assertNotEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name2', 'value1', False, False, []))
+        self.assertNotEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name1', 'value2', False, False, []))
+        self.assertNotEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name1', 'value1', True, False, []))
+        self.assertNotEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name1', 'value1', False, True, []))
+        self.assertNotEqual(
+            Condition('name1', 'value1', False, False, []),
+            Condition('name1', 'value1', False, False, [Condition()]))
+        self.assertNotEqual(Condition(), 'another type')
+
+    def test_from_string(self):
+        text = '<CONDITION />'
+        condition = Condition.from_string(text)
+        right = Condition()
+        self.assertEqual(condition, right)
+
+    def test_from_string_name(self):
+        text = '<CONDITION name="test" />'
+        condition = Condition.from_string(text)
+        right = Condition(name = 'test')
+        self.assertEqual(condition, right)
+
+    def test_from_string_value(self):
+        text = '<CONDITION value="test" />'
+        condition = Condition.from_string(text)
+        right = Condition(value = 'test')
+        self.assertEqual(condition, right)
+
+    def test_from_string_reverse(self):
+        text = '<CONDITION not="yes" />'
+        condition = Condition.from_string(text)
+        right = Condition(reverse = True)
+        self.assertEqual(condition, right)
+
+    def test_from_string_regex(self):
+        text = '<CONDITION regex="yes" />'
+        condition = Condition.from_string(text)
+        right = Condition(regex = True)
+        self.assertEqual(condition, right)
+
+    def test_from_string_sub_elements(self):
+        text = '<CONDITION><PERMISSION /><RETURN /></CONDITION>'
+        condition = Condition.from_string(text)
+        right = Condition(sub_elements = [Permission(), Return()])
+        self.assertEqual(condition, right)
+
+    def test_from_string_full(self):
+        text = '''
+<CONDITION name="name" value="value" not="yes" regex="yes">
+  <PERMISSION />
+  <RETURN />
+</CONDITION>'''
+        condition = Condition.from_string(text)
+        right = Condition('name', 'value', True, True,
+                          [Permission(), Return()])
+        self.assertEqual(condition, right)
+
+    def test_from_lxml(self):
+        text = '<CONDITION />'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition()
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_name(self):
+        text = '<CONDITION name="test" />'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition(name = 'test')
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_value(self):
+        text = '<CONDITION value="test" />'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition(value = 'test')
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_reverse(self):
+        text = '<CONDITION not="yes" />'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition(reverse = True)
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_regex(self):
+        text = '<CONDITION regex="yes" />'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition(regex = True)
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_sub_elements(self):
+        text = '<CONDITION><PERMISSION /><RETURN /></CONDITION>'
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition(sub_elements = [Permission(), Return()])
+        self.assertEqual(condition, right)
+
+    def test_from_lxml_full(self):
+        text = '''
+<CONDITION name="name" value="value" not="yes" regex="yes">
+  <PERMISSION />
+  <RETURN />
+</CONDITION>'''
+        condition = Condition.from_lxml_element(etree.XML(text))
+        right = Condition('name', 'value', True, True,
+                          [Permission(), Return()])
+        self.assertEqual(condition, right)
+
+    def test_to_string(self):
+        condition = Condition()
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_name(self):
+        condition = Condition(name = 'name')
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_value(self):
+        condition = Condition(value = 'value')
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_reverse(self):
+        condition = Condition(reverse = True)
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_regex(self):
+        condition = Condition(regex = True)
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_sub_elements(self):
+        condition = Condition(sub_elements = [Permission(), Return()])
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_string_full(self):
+        condition = Condition('name', 'value', True, True,
+                              [Permission(), Return()])
+        copy = Condition.from_string(str(condition))
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml(self):
+        condition = Condition()
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_name(self):
+        condition = Condition(name = 'name')
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_value(self):
+        condition = Condition(value = 'value')
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_reverse(self):
+        condition = Condition(reverse = True)
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_regex(self):
+        condition = Condition(regex = True)
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_sub_elements(self):
+        condition = Condition(sub_elements = [Permission(), Return()])
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_to_lxml_full(self):
+        condition = Condition('name', 'value', True, True,
+                              [Permission(), Return()])
+        copy = Condition.from_lxml_element(condition.to_lxml_element())
+        self.assertEqual(condition, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, Condition.from_string, text)
+        self.assertRaises(AttributeError, Condition.from_lxml_element,
+                          etree.XML(text))
+
+class BadMarkupTest(unittest.TestCase):
+    def condition_test(self):
+        text = '''
+<CONDITION custom="unknown">
+  <PERMISSION />
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</CONDITION>'''
+        condition = Condition.from_string(text)
+        tree = condition.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
+if __name__ == '__main__':
+    unittest.main()



commit f4ef782aa3024edf332fd0ec8f25dcfc4a5c127f
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Aug 15 22:26:28 2009 +0200

    Control lib implements security files.

diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
index 7b9b05b..1cd1141 100644
--- a/misc/py_control_client/MyServer/pycontrollib/security.py
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -20,7 +20,27 @@ from lxml import etree
 from definition import Definition
 
 class SecurityElement():
-    pass
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag == 'CONDITION':
+            return Condition.from_lxml_element(root)
+        elif root.tag == 'PERMISSION':
+            return Permission.from_lxml_element(root)
+        elif root.tag == 'RETURN':
+            return Return.from_lxml_element(root)
+        elif root.tag == 'DEFINE':
+            return Definition.from_lxml_element(root)
+        else:
+            raise AttributeError(
+                '{0} is not allowed in security files'.format(root.tag))
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce security element by parsing a string.'''
+        return SecurityElement.from_lxml_element(etree.XML(text))
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
 class Condition(SecurityElement):
     def __init__(self, name = None, value = None, reverse = None, regex = None,
@@ -113,14 +133,278 @@ class Condition(SecurityElement):
 
     @staticmethod
     def from_string(text):
-        '''Factory to produce definition tree by parsing a string.'''
-        return DefinitionTree.from_lxml_element(etree.XML(text))
-
-    def __str__(self):
-        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+        '''Factory to produce condition element by parsing a string.'''
+        return Condition.from_lxml_element(etree.XML(text))
 
 class Permission(SecurityElement):
-    pass
+    def __init__(self, read = None, execute = None, browse = None,
+                 delete = None, write = None):
+        self.set_read(read)
+        self.set_execute(execute)
+        self.set_browse(browse)
+        self.set_delete(delete)
+        self.set_write(write)
+        self.custom_attributes = {}
+        self.custom = []
+
+    def set_read(self, read):
+        self.read = read
+
+    def get_read(self):
+        return self.read
+
+    def set_execute(self, execute):
+        self.execute = execute
+
+    def get_execute(self):
+        return self.execute
+
+    def set_browse(self, browse):
+        self.browse = browse
+
+    def get_browse(self):
+        return self.browse
+
+    def set_delete(self, delete):
+        self.delete = delete
+
+    def get_delete(self):
+        return self.delete
+
+    def set_write(self, write):
+        self.write = write
+
+    def get_write(self):
+        return self.write
+
+    def to_lxml_element(self):
+        root = etree.Element('PERMISSION')
+        if self.read is not None:
+            root.set('READ', self.read)
+        if self.execute is not None:
+            root.set('EXECUTE', self.execute)
+        if self.browse is not None:
+            root.set('BROWSE', self.browse)
+        if self.delete is not None:
+            root.set('DELETE', self.delete)
+        if self.write is not None:
+            root.set('WRITE', self.write)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+        for element in self.custom:
+            root.append(element)
+        return root
+
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag != 'PERMISSION':
+            raise AttributeError('Expected PERMISSION tag.')
+        custom_attrib = root.attrib
+        read = custom_attrib.pop('READ', None)
+        execute = custom_attrib.pop('EXECUTE', None)
+        browse = custom_attrib.pop('BROWSE', None)
+        delete = custom_attrib.pop('DELETE', None)
+        write = custom_attrib.pop('WRITE', None)
+        custom = list(root)
+        permission = Permission(read, execute, browse, delete, write)
+        permission.custom_attrib = custom_attrib
+        permission.custom = custom
+        return permission
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce permission element by parsing a string.'''
+        return Permission.from_lxml_element(etree.XML(text))
+
+class User(SecurityElement):
+    def __init__(self, name = None, password = None, read = None,
+                 execute = None, browse = None, delete = None, write = None):
+        self.set_name(name)
+        self.set_password(password)
+        self.set_read(read)
+        self.set_execute(execute)
+        self.set_browse(browse)
+        self.set_delete(delete)
+        self.set_write(write)
+        self.custom_attributes = {}
+        self.custom = []
+
+    def set_name(self, name):
+        self.name = name
+
+    def get_name(self):
+        return self.name
+
+    def set_password(self, password):
+        self.password = password
+
+    def get_password(self):
+        return self.password
+
+    def set_read(self, read):
+        self.read = read
+
+    def get_read(self):
+        return self.read
+
+    def set_execute(self, execute):
+        self.execute = execute
+
+    def get_execute(self):
+        return self.execute
+
+    def set_browse(self, browse):
+        self.browse = browse
+
+    def get_browse(self):
+        return self.browse
+
+    def set_delete(self, delete):
+        self.delete = delete
+
+    def get_delete(self):
+        return self.delete
+
+    def set_write(self, write):
+        self.write = write
+
+    def get_write(self):
+        return self.write
+
+    def to_lxml_element(self):
+        root = etree.Element('PERMISSION')
+        if self.name is not None:
+            root.set('name', self.name)
+        if self.password is not None:
+            root.set('password', self.password)
+        if self.read is not None:
+            root.set('READ', self.read)
+        if self.execute is not None:
+            root.set('EXECUTE', self.execute)
+        if self.browse is not None:
+            root.set('BROWSE', self.browse)
+        if self.delete is not None:
+            root.set('DELETE', self.delete)
+        if self.write is not None:
+            root.set('WRITE', self.write)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+        for element in self.custom:
+            root.append(element)
+        return root
+
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag != 'USER':
+            raise AttributeError('Expected USER tag.')
+        custom_attrib = root.attrib
+        name = custom_attrib.pop('name', None)
+        password = custom_attrib.pop('password', None)
+        read = custom_attrib.pop('READ', None)
+        execute = custom_attrib.pop('EXECUTE', None)
+        browse = custom_attrib.pop('BROWSE', None)
+        delete = custom_attrib.pop('DELETE', None)
+        write = custom_attrib.pop('WRITE', None)
+        custom = list(root)
+        permission = Permission(name, password, read, execute, browse,
+                                delete, write)
+        permission.custom_attrib = custom_attrib
+        permission.custom = custom
+        return permission
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce user element by parsing a string.'''
+        return Permission.from_lxml_element(etree.XML(text))
 
 class Return(SecurityElement):
-    pass
+    def __init__(self, value = None):
+        self.set_value(value)
+        self.custom_attributes = {}
+        self.custom = []
+
+    def set_value(self, value):
+        self.value = value
+
+    def get_value(self):
+        return self.value
+
+    def to_lxml_element(self):
+        root = etree.Element('RETURN')
+        if self.value is not None:
+            root.set('value', self.value)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+        for element in self.custom:
+            root.append(element)
+        return root
+
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag != 'RETURN':
+            raise AttributeError('Expected RETURN tag.')
+        custom_attrib = root.attrib
+        value = custom_attrib.pop('value', None)
+        custom = list(root)
+        ret = Return(value)
+        ret.custom_attrib = custom_attrib
+        ret.custom = custom
+        return ret
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce return element by parsing a string.'''
+        return Return.from_lxml_element(etree.XML(text))
+
+class SecurityList():
+    def __init__(self, elements = []):
+        for elements in elements:
+            self.add_element(element)
+        self.custom = []
+        self.custom_attrib = {}
+
+    def get_elements(self):
+        return self.elements
+
+    def add_element(self, element, index = None):
+        if index is None:
+            self.elements.append(element)
+        else:
+            self.elements.insert(index, element)
+
+    def remove_element(self, index):
+        self.elements.pop(index)
+
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag != 'SECURITY':
+            raise AttributeError('Expected SECURITY tag.')
+        elements = []
+        custom = []
+        for element in list(root):
+            try:
+                elements.append(SecurityElement.from_lxml_element(element))
+            else:
+                custom.append(element)
+        security_list = SecurityList(elements)
+        security_list.custom = custom
+        security_list.custom_attrib = root.attrib
+        return security_list
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce security list by parsing a string.'''
+        return SecurityList.from_lxml_element(etree.XML(text))
+
+    def to_lxml_element(self):
+        root = etree.Element('SECURITY')
+        for element in self.elements:
+            root.append(element.to_lxml_element())
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+        for element in self.custom:
+            root.append(element)
+        return root
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)



commit 6f59a090d44198c0190a6106641bbff7c7cde845
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Aug 14 20:53:14 2009 +0200

    Added condition to control lib.

diff --git a/misc/py_control_client/MyServer/pycontrollib/security.py 
b/misc/py_control_client/MyServer/pycontrollib/security.py
new file mode 100644
index 0000000..7b9b05b
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/security.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+from definition import Definition
+
+class SecurityElement():
+    pass
+
+class Condition(SecurityElement):
+    def __init__(self, name = None, value = None, reverse = None, regex = None,
+                 sub_elements = []):
+        self.set_name(name)
+        self.set_value(value)
+        self.set_reverse(reverse)
+        self.set_regex(regex)
+        self.custom_attributes = {}
+        self.custom = []
+        self.sub_elements = []
+        for element in sub_elements:
+            self.add_sub_element(element)
+
+    def set_name(self, name):
+        self.name = name
+
+    def get_name(self):
+        return self.name
+
+    def set_value(value):
+        self.value = value
+
+    def get_value(self):
+        return self.value
+
+    def set_reverse(self, reverse):
+        self.reverse = reverse
+
+    def get_reverse(self):
+        return self.reverse
+
+    def set_regex(self):
+        self.regex = regex
+
+    def get_regex(self):
+        return self.regex
+
+    def add_sub_element(self, element):
+        if isinstance(element, Definition) or \
+                isinstance(element, SecurityElement):
+            self.sub_elements.append(element)
+        else:
+            self.custom.append(element)
+
+    def get_sub_elements(self):
+        return self.sub_elements
+
+    def get_sub_element(self, index):
+        return self.sub_elements[index]
+
+    def to_lxml_element(self):
+        root = etree.Element('CONDITION')
+        if self.name is not None:
+            root.set('name', self.name)
+        if self.value is not None:
+            root.set('value', self.value)
+        if self.reverse is not None:
+            root.set('not', 'yes' if self.reverse else 'no')
+        if self.regex is not None:
+            root.set('regex', 'yes' if self.reverse else 'no')
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+        for element in self.sub_elements:
+            root.append(element.to_lxml_element())
+        for element in self.custom:
+            root.append(element)
+        return root
+
+    @staticmethod
+    def from_lxml_element(root):
+        if root.tag != 'CONDITION':
+            raise AttributeError('Expected CONDITION tag.')
+        custom_attrib = root.attrib
+        name = custom_attrib.pop('name', None)
+        value = custom_attrib.pop('value', None)
+        reverse = custom_attrib.pop('not', None)
+        regex = custom_attrib.pop('regex', None)
+        custom = []
+        sub_elements = []
+        for child in list(root):
+            try:
+                sub_elements.append(SecurityElement.from_lxml_element(child))
+            except:
+                custom.append(child)
+        condition = Condition(name, value, reverse, regex, sub_elements)
+        condition.custom = custom
+        condition.custom_attrib = custom_attrib
+        return condition
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce definition tree by parsing a string.'''
+        return DefinitionTree.from_lxml_element(etree.XML(text))
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+class Permission(SecurityElement):
+    pass
+
+class Return(SecurityElement):
+    pass



commit fc81d1bb032e279a2e7436fe95780728f26ee738
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Aug 14 16:32:45 2009 +0200

    Added filesystem browser to GUI for security files.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 70febbc..c05e312 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -30,6 +30,7 @@ from MyServer.GUI.ConnectionWindow import Connection
 from MyServer.GUI.DefinitionWidgets import DefinitionTable, DefinitionTreeView
 from MyServer.GUI.MIMEWidgets import MimeTable, MimeTreeView
 from MyServer.GUI.VHostWidgets import VHostTable, VHostTreeView
+from MyServer.GUI.BrowserWidgets import BrowserTable
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -41,6 +42,7 @@ class PyGTKControl():
         self.construct_options()
         self.construct_mime()
         self.construct_vhosts()
+        self.construct_browser()
         self.chooser = None # Active file chooser
         # path of currently edited files
         self.config_path = self.mime_path = self.vhost_path = None
@@ -437,5 +439,14 @@ class PyGTKControl():
 
         self.widgets.get_widget('notebook').show_all()
 
+    def construct_browser(self):
+        '''Constructs file browser.'''
+        table = BrowserTable()
+        
+        self.widgets.get_widget('notebook').append_page(
+            table, gtk.Label('File browser'))
+
+        self.widgets.get_widget('notebook').show_all()
+
 PyGTKControl()
 gtk.main()
diff --git a/misc/py_control_client/MyServer/GUI/BrowserWidgets.py 
b/misc/py_control_client/MyServer/GUI/BrowserWidgets.py
new file mode 100644
index 0000000..4d540fc
--- /dev/null
+++ b/misc/py_control_client/MyServer/GUI/BrowserWidgets.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+from gtk import gdk
+import gobject
+from MyServer.pycontrollib.browser import LocalFileBrowser
+
+class BrowserTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gdk.Pixbuf,
+                gobject.TYPE_STRING))
+        self.model = self.get_model()
+        renderer = gtk.CellRendererPixbuf()
+        column = gtk.TreeViewColumn()
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'pixbuf', 0)
+        self.append_column(column)
+        renderer = gtk.CellRendererText()
+        column = gtk.TreeViewColumn('File')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 1)
+        self.append_column(column)
+
+        self.browser = LocalFileBrowser()
+        self.browser.show_hidden(False)
+        icon_theme = gtk.icon_theme_get_default()
+        self.dir_icon = icon_theme.load_icon('gtk-directory', 16, 0)
+        self.update()
+        self.connect('row-activated', self.change_dir)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+    def update(self):
+        '''Show contents of current directory.'''
+        self.model.clear()
+        for d in self.browser.list_dir():
+            self.model.append((self.dir_icon, d, ))
+
+    def change_dir(self, widget, path, view_column):
+        self.browser.change_dir(self.model[path][1])
+        self.update()
+
+class BrowserTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, 2, 1)
+
+        self.browser_tree = BrowserTreeView()
+
+        def set_show_hidden(button):
+            self.browser_tree.browser.show_hidden(button.get_active())
+            self.browser_tree.update()
+        hide_check = gtk.CheckButton('Show hidden directories.')
+        hide_check.connect('toggled', set_show_hidden)
+
+        self.attach(hide_check, 0, 1, 0, 1, yoptions = gtk.FILL)
+        self.attach(self.browser_tree.scroll, 0, 1, 1, 2)
diff --git a/misc/py_control_client/MyServer/pycontrollib/browser.py 
b/misc/py_control_client/MyServer/pycontrollib/browser.py
new file mode 100644
index 0000000..63b8317
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/browser.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 os
+
+class FileBrowser():
+    'Abstract file browser class.'
+    def list_dir(self):
+        raise NotImplementedError()
+
+    def list_files(self):
+        raise NotImplementedError()
+    
+    def get_file(self, path):
+        raise NotImplementedError()
+    
+    def put_file(self, path, text):
+        raise NotImplementedError()
+    
+    def get_path(self):
+        raise NotImplementedError()
+
+    def change_dir(self, path):
+        raise NotImplementedError()
+
+    def show_hidden(self, show):
+        raise NotImplementedError()
+
+class LocalFileBrowser(FileBrowser):
+    'Local file browser class.'
+    def __init__(self, path = os.path.expanduser('~')):
+        self.path = path
+        self.show = False
+
+    def list_dir(self):
+        return ['..'] + sorted(
+            [path for path in os.listdir(self.path) if \
+                 not os.path.isfile(os.path.join(self.path, path)) \
+                and (self.show or not path.startswith('.'))])
+
+    def list_files(self):
+        return ['..'] + sorted(
+            [path for path in os.listdir(self.path) if \
+                 os.path.isfile(os.path.join(self.path, path)) \
+                and (self.show or not path.startswith('.'))])
+
+    def get_file(self, path):
+        with open(os.path.join(self.path, path)) as f:
+            return f.read()
+
+    def put_file(self, path, text):
+        with open(os.path.join(self.path, path), 'w') as f:
+            f.write(text) 
+
+    def get_path(self):
+        return self.path
+
+    def change_dir(self, path):
+        if path == '..':
+            self.path = os.path.split(self.path)[0]
+        else:
+            self.path = os.path.join(self.path, path)
+
+    def show_hidden(self, show):
+        self.show = show



commit 6b4599d38b577a26c64e31e7630c63c066f6f9f8
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Aug 13 20:08:58 2009 +0200

    Added GUI interface to remove mimes and vhosts.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 708f3fd..70febbc 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -289,6 +289,11 @@ class PyGTKControl():
             mime_lists[mime_list] = []
         tree.get_model().append(('', {}, mime_lists, [], [], {}, ))
 
+    def on_remove_mime_type_menu_item_activate(self, widget):
+        '''Removes selected MIME type.'''
+        table, tree = self.mime_tab[0]
+        table.remove_current(tree)
+
     def on_add_vhost_menu_item_activate(self, widget):
         '''Adds a new VHost.'''
         table, tree = self.vhost_tab
@@ -297,6 +302,11 @@ class PyGTKControl():
             vhost_lists[vhost_list[0]] = []
         tree.get_model().append(('', {}, vhost_lists, [], [], {}, ))
 
+    def on_remove_vhost_menu_item_activate(self, widget):
+        '''Removes selected VHost.'''
+        table, tree = self.vhost_tab
+        table.remove_current(tree)
+
     def on_add_definition_to_mime_menu_item_activate(self, widget):
         '''Adds a definition to currently selected MIME type.'''
         table, tree = self.mime_tab[1]
@@ -307,6 +317,11 @@ class PyGTKControl():
         table, tree = self.vhost_tab
         table.add_log()
 
+    def on_remove_log_from_vhost_menu_item_activate(self, widget):
+        '''Removes selected log from currently selected VHost.'''
+        table, tree = self.vhost_tab
+        table.remove_log()
+
     def set_up_config(self, config):
         '''Reads server configuration from given config instance.'''
         self.on_new_config_menu_item_activate()
@@ -412,7 +427,7 @@ class PyGTKControl():
         '''Reads vhost options from file and prepares GUI.'''
         panels = gtk.HPaned()
         tree = VHostTreeView()
-        panels.pack1(tree.scroll, False, False)
+        panels.pack1(tree.scroll, True, False)
         table = VHostTable(tree)
         panels.pack2(table.scroll, True, False)
 
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 10af6b3..63db0cc 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -382,6 +382,21 @@
                       </widget>
                     </child>
                     <child>
+                      <widget class="GtkImageMenuItem" 
id="remove_mime_type_menu_item">
+                        <property name="label">Remove MIME type</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_remove_mime_type_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="remove_mime_type_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-remove</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
                       <widget class="GtkImageMenuItem" 
id="add_definition_to_mime_menu_item">
                         <property name="label">Add definition to MIME 
type</property>
                         <property name="visible">True</property>
@@ -424,6 +439,21 @@
                       </widget>
                     </child>
                     <child>
+                      <widget class="GtkImageMenuItem" 
id="remove_vhost_menu_item">
+                        <property name="label">Remove VHost</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_remove_vhost_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="remove_vhost_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-remove</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
                       <widget class="GtkImageMenuItem" 
id="add_log_to_vhost_menu_item">
                         <property name="label">Add log to VHost</property>
                         <property name="visible">True</property>
@@ -438,6 +468,21 @@
                         </child>
                       </widget>
                     </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="remove_log_from_vhost_menu_item">
+                        <property name="label">Remove log from VHost</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_remove_log_from_vhost_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" 
id="remove_log_from_vhost_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-remove</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
                   </widget>
                 </child>
               </widget>
diff --git a/misc/py_control_client/MyServer/GUI/MIMEWidgets.py 
b/misc/py_control_client/MyServer/GUI/MIMEWidgets.py
index 8a9d338..2f3ed7e 100644
--- a/misc/py_control_client/MyServer/GUI/MIMEWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/MIMEWidgets.py
@@ -126,6 +126,13 @@ class MimeTable(gtk.Table):
             self.attach(gtk.Label(), 0, 1, i + 2, i + 3)
             i += 3
 
+    def remove_current(self, tree):
+        '''Remove currently selected MIME type.'''
+        if self.last_selected is not None:
+            model = tree.get_model()
+            model.remove(self.last_selected)
+            self.clear()
+
     def clear(self):
         '''Clear input widgets (including connected definition widgets).'''
         for entry, check in self.attributes.itervalues():
diff --git a/misc/py_control_client/MyServer/GUI/VHostWidgets.py 
b/misc/py_control_client/MyServer/GUI/VHostWidgets.py
index 204f306..165b1df 100644
--- a/misc/py_control_client/MyServer/GUI/VHostWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/VHostWidgets.py
@@ -409,7 +409,20 @@ class VHostTable(gtk.Table):
 
     def add_log(self):
         '''Add a log to currently selected VHost.'''
-        self.log_model.append(('', [], ('', False, )))
+        self.log_model.append(('', [], ('', False, ), [], {}, ))
+
+    def remove_log(self):
+        '''Remove currently selected log.'''
+        if self.log_tree.last_selected is not None:
+            self.log_model.remove(self.log_tree.last_selected)
+            self.log_tree.clear()
+
+    def remove_current(self, tree):
+        '''Remove currently selected VHost.'''
+        if self.last_selected is not None:
+            model = tree.get_model()
+            model.remove(self.last_selected)
+            self.clear()
 
     def clear(self):
         '''Clear input widgets.'''



commit f3636a24d3828ae7c721380b68acb37ff44d758b
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Aug 13 15:43:46 2009 +0200

    Fix GUI segfault.

diff --git a/misc/py_control_client/MyServer/GUI/VHostWidgets.py 
b/misc/py_control_client/MyServer/GUI/VHostWidgets.py
index f248f97..204f306 100644
--- a/misc/py_control_client/MyServer/GUI/VHostWidgets.py
+++ b/misc/py_control_client/MyServer/GUI/VHostWidgets.py
@@ -386,6 +386,7 @@ class VHostTable(gtk.Table):
             model, selected = tree.get_selection().get_selected()
             if selected is not None:
                 model.remove(selected)
+                tree.last_selected = None
         remove_stream_button = gtk.Button('Remove stream')
         remove_stream_button.connect('clicked', remove_selected, stream_tree)
         remove_filter_button = gtk.Button('Remove filter')



commit 26f614483d0877a0968819ec45a0e68505ea3a85
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 21:27:37 2009 +0200

    Updated setup script.

diff --git a/misc/py_control_client/setup.py b/misc/py_control_client/setup.py
index 17415cd..f327e72 100644
--- a/misc/py_control_client/setup.py
+++ b/misc/py_control_client/setup.py
@@ -19,4 +19,4 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 from distutils.core import setup
 setup(name = 'MyServer', version = '0.0',
       py_modules = ['MyServer.__init__'],
-      packages = ['MyServer/pycontrol', 'MyServer/pycontrollib'])
+      packages = ['MyServer/pycontrol', 'MyServer/pycontrollib', 
'MyServer.GUI'])



commit e096fc5a40603a7c8ce97bb996430fcc06e80534
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 21:26:51 2009 +0200

    Moved GUI widgets to library.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 477b78e..708f3fd 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -20,16 +20,16 @@ from __future__ import print_function
 import gtk
 import gtk.glade
 import gobject
-import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
 from MyServer.pycontrollib.mimetypes import MIMETypes
 from MyServer.pycontrollib.controller import Controller
 from MyServer.pycontrollib.vhost import VHosts
-from AboutWindow import About
-from ConnectionWindow import Connection
-from DefinitionWidgets import DefinitionTable, DefinitionTreeView
-from MIMEWidgets import MimeTable, MimeTreeView
-from VHostWidgets import VHostTable, VHostTreeView
+from MyServer.GUI import GUIConfig
+from MyServer.GUI.AboutWindow import About
+from MyServer.GUI.ConnectionWindow import Connection
+from MyServer.GUI.DefinitionWidgets import DefinitionTable, DefinitionTreeView
+from MyServer.GUI.MIMEWidgets import MimeTable, MimeTreeView
+from MyServer.GUI.VHostWidgets import VHostTable, VHostTreeView
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
diff --git a/misc/PyGTK_Control/AboutWindow.py 
b/misc/py_control_client/MyServer/GUI/AboutWindow.py
similarity index 100%
rename from misc/PyGTK_Control/AboutWindow.py
rename to misc/py_control_client/MyServer/GUI/AboutWindow.py
diff --git a/misc/PyGTK_Control/ConnectionWindow.py 
b/misc/py_control_client/MyServer/GUI/ConnectionWindow.py
similarity index 100%
rename from misc/PyGTK_Control/ConnectionWindow.py
rename to misc/py_control_client/MyServer/GUI/ConnectionWindow.py
diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/py_control_client/MyServer/GUI/DefinitionWidgets.py
similarity index 100%
rename from misc/PyGTK_Control/DefinitionWidgets.py
rename to misc/py_control_client/MyServer/GUI/DefinitionWidgets.py
diff --git a/misc/PyGTK_Control/GUIConfig.py 
b/misc/py_control_client/MyServer/GUI/GUIConfig.py
similarity index 100%
rename from misc/PyGTK_Control/GUIConfig.py
rename to misc/py_control_client/MyServer/GUI/GUIConfig.py
diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/py_control_client/MyServer/GUI/MIMEWidgets.py
similarity index 100%
rename from misc/PyGTK_Control/MIMEWidgets.py
rename to misc/py_control_client/MyServer/GUI/MIMEWidgets.py
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/py_control_client/MyServer/GUI/VHostWidgets.py
similarity index 100%
rename from misc/PyGTK_Control/VHostWidgets.py
rename to misc/py_control_client/MyServer/GUI/VHostWidgets.py
diff --git a/misc/py_control_client/MyServer/GUI/__init__.py 
b/misc/py_control_client/MyServer/GUI/__init__.py
new file mode 100644
index 0000000..8fa2136
--- /dev/null
+++ b/misc/py_control_client/MyServer/GUI/__init__.py
@@ -0,0 +1,2 @@
+#
+



commit 26eb5c3ecba6ede2197b9fe75b6992d467c5d4b9
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 21:14:36 2009 +0200

    Better GUI look.

diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
index d171cee..8a9d338 100644
--- a/misc/PyGTK_Control/MIMEWidgets.py
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -125,7 +125,7 @@ class MimeTable(gtk.Table):
             self.attach(remove_button, 0, 1, i + 1, i + 2, yoptions = gtk.FILL)
             self.attach(gtk.Label(), 0, 1, i + 2, i + 3)
             i += 3
-            
+
     def clear(self):
         '''Clear input widgets (including connected definition widgets).'''
         for entry, check in self.attributes.itervalues():
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 0ec6c1c..477b78e 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -91,22 +91,22 @@ class PyGTKControl():
         '''Get MIME config from remote server.'''
         if self.controller is not None:
             self.set_up_mime(self.controller.get_MIME_type_configuration())
-            
+
     def on_get_vhost_menu_item_activate(self, widget):
         '''Get VHost config from remote server.'''
         if self.controller is not None:
             self.set_up_vhost(self.controller.get_vhost_configuration())
-            
+
     def on_put_config_menu_item_activate(self, widget):
         '''Put server config on remote server.'''
         if self.controller is not None:
             self.controller.put_server_configuration(self.get_current_config())
-            
+
     def on_put_mime_menu_item_activate(self, widget):
         '''Put MIME config on remote server.'''
         if self.controller is not None:
             
self.controller.put_MIME_type_configuration(self.get_current_mime())
-            
+
     def on_put_vhost_menu_item_activate(self, widget):
         '''Put VHost config on remote server.'''
         if self.controller is not None:
@@ -226,7 +226,7 @@ class PyGTKControl():
             config = config_method()
             with open(getattr(self, path), 'w') as f:
                 f.write(str(config))
-        
+
     def on_save_config_menu_item_activate(self, widget):
         '''Save server configuration to file.'''
         self.generic_save(
@@ -396,12 +396,12 @@ class PyGTKControl():
 
         panels = gtk.HPaned()
         tree = MimeTreeView()
-        panels.pack1(tree.scroll, False, False)
+        panels.pack1(tree.scroll, True, False)
         table = MimeTable(tree, def_tree, def_table)
-        panels.pack2(table, True, False)
+        panels.pack2(table, False, False)
 
         vpanels.pack1(panels)
-        
+
         self.mime_tab = ((table, tree, ), (def_table, def_tree), )
         self.widgets.get_widget('notebook').append_page(
             vpanels, gtk.Label('MIME Type'))
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
index 8069a35..f248f97 100644
--- a/misc/PyGTK_Control/VHostWidgets.py
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -137,7 +137,7 @@ class LogTreeView(gtk.TreeView):
         self.scroll.set_shadow_type(gtk.SHADOW_OUT)
         self.scroll.set_border_width(5)
         self.scroll.add(self)
-        
+
         self.last_selected = None
         self.connect('cursor-changed', self.cursor_changed)
 
@@ -148,7 +148,7 @@ class LogTreeView(gtk.TreeView):
         entry, check = self.log_type
         entry.set_text('')
         check.set_active(False)
-        
+
     def save_changed(self):
         self.stream_tree.save_changed()
         model = self.stream_tree.get_model()
@@ -291,7 +291,7 @@ class VHostTable(gtk.Table):
             self.attach(entry, 1, 2, i, i + 1, yoptions = gtk.FILL)
             self.attach(check, 2, 3, i, i + 1, gtk.FILL, gtk.FILL)
             i += 1
-            
+
         self.vhost_lists = {}
         for vhost_list in GUIConfig.vhost_lists: # Add attribute lists
             name = vhost_list[0]
@@ -362,7 +362,7 @@ class VHostTable(gtk.Table):
         self.attach(cycle_check, 2, 3, i + 4, i + 5, gtk.FILL, gtk.FILL)
         self.attach(cycle_gzip_label, 0, 1, i + 5, i + 6, yoptions = gtk.FILL)
         self.attach(cycle_gzip_check, 2, 3, i + 5, i + 6, gtk.FILL, gtk.FILL)
-        
+
         filter_tree = FilterTreeView()
         stream_tree = StreamTreeView(filter_tree,
                                      (cycle_entry, cycle_check, ),
@@ -409,7 +409,7 @@ class VHostTable(gtk.Table):
     def add_log(self):
         '''Add a log to currently selected VHost.'''
         self.log_model.append(('', [], ('', False, )))
-            
+
     def clear(self):
         '''Clear input widgets.'''
         for entry, check in self.attributes.itervalues():



commit ce56783eff4746a0bd3aee5f3e00d3aa56390652
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 21:11:23 2009 +0200

    Working keeping unknown things in mimes and vhosts.

diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
index a6f0938..d171cee 100644
--- a/misc/PyGTK_Control/MIMEWidgets.py
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -27,7 +27,9 @@ class MimeTreeView(gtk.TreeView):
                 gobject.TYPE_STRING, # mime name
                 gobject.TYPE_PYOBJECT, # mime single attributes
                 gobject.TYPE_PYOBJECT, # mime attribute lists
-                gobject.TYPE_PYOBJECT)) # mime definitions
+                gobject.TYPE_PYOBJECT, # mime definitions
+                gobject.TYPE_PYOBJECT, # mime custom
+                gobject.TYPE_PYOBJECT)) # mime custom attrib
         model = self.get_model()
         def mime_edited_handler(cell, path, text, data):
             model = data
@@ -61,7 +63,9 @@ class MimeTreeView(gtk.TreeView):
             model.append((getattr(mime, 'get_' + GUIConfig.mime_name)(),
                           attributes,
                           mime_lists,
-                          mime.get_definitions(), ))
+                          mime.get_definitions(),
+                          mime.custom,
+                          mime.custom_attrib, ))
 
 class MimeTable(gtk.Table):
     def __init__(self, tree, def_tree, def_table):
@@ -179,6 +183,8 @@ class MimeTable(gtk.Table):
         while i is not None: # iterate over MIME types
             row = model[i]
             mime = MIMEType(row[0], definitions = row[3])
+            mime.custom = row[4]
+            mime.custom_attrib = row[5]
             for attribute in row[1]:
                 text, enabled = row[1][attribute]
                 if enabled:
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index ce8b533..0ec6c1c 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -46,6 +46,10 @@ class PyGTKControl():
         self.config_path = self.mime_path = self.vhost_path = None
         # remembered unhandled parts of configs
         self.config_custom = []
+        self.mime_custom = []
+        self.vhost_custom = []
+        self.mime_custom_attrib = {}
+        self.vhost_custom_attrib = {}
         self.controller = None
 
     def on_window_destroy(self, widget):
@@ -124,6 +128,8 @@ class PyGTKControl():
         '''Clears MIME configuration.'''
         if widget is not None:
             self.mime_path = None
+        self.mime_custom = []
+        self.mime_custom_attrib = {}
         table, tree = self.mime_tab[0]
         table.clear()
         tree.get_model().clear()
@@ -132,11 +138,13 @@ class PyGTKControl():
         '''Clears VHost configuration.'''
         if widget is not None:
             self.vhost_path = None
+        self.vhost_custom = []
+        self.vhost_custom_attrib = {}
         table, tree = self.vhost_tab
         table.clear()
         tree.get_model().clear()
 
-    def generic_open(self, dialog, config_type, set_up_method):
+    def generic_open(self, dialog, path, config_type, set_up_method):
         '''Open configuration file.'''
         if self.chooser is not None:
             self.chooser.destroy()
@@ -146,8 +154,8 @@ class PyGTKControl():
                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
-                self.config_path = self.chooser.get_filename()
-                with open(self.config_path) as f:
+                setattr(self, path, self.chooser.get_filename())
+                with open(getattr(self, path)) as f:
                     conf = config_type.from_string(f.read())
                 set_up_method(conf)
             self.chooser.destroy()
@@ -157,19 +165,19 @@ class PyGTKControl():
     def on_open_config_menu_item_activate(self, widget):
         '''Open local server configuration file.'''
         self.generic_open(
-            'Open server configuration file.',
+            'Open server configuration file.', 'config_path',
             MyServerConfig, self.set_up_config)
 
     def on_open_mime_menu_item_activate(self, widget):
         '''Open local MIME configuration file.'''
         self.generic_open(
-            'Open MIME configuration file.',
+            'Open MIME configuration file.', 'mime_path',
             MIMETypes, self.set_up_mime)
 
     def on_open_vhost_menu_item_activate(self, widget):
         '''Open local VHost configuration file.'''
         self.generic_open(
-            'Open VHost configuration file.',
+            'Open VHost configuration file.', 'vhost_path',
             VHosts, self.set_up_vhost)
 
     def generic_save_as(self, dialog, widget, path, save_method):
@@ -254,13 +262,19 @@ class PyGTKControl():
         '''Returns current MIME configuration as MIMETypes instance.'''
         table, tree = self.mime_tab[0]
         mimes = table.make_def(tree)
-        return MIMETypes(mimes)
+        config = MIMETypes(mimes)
+        config.custom = self.mime_custom
+        config.custom_attrib = self.mime_custom_attrib
+        return config
 
     def get_current_vhost(self):
         '''Returns current VHost configuration as VHosts instance.'''
         table, tree = self.vhost_tab
-        mimes = table.make_def(tree)
-        return VHosts(mimes)
+        vhosts = table.make_def(tree)
+        config = VHosts(vhosts)
+        config.custom = self.vhost_custom
+        config.custom_attrib = self.vhost_custom_attrib
+        return config
 
     def on_add_unknown_definition_menu_item_activate(self, widget):
         '''Adds a new definition to unknown tab.'''
@@ -273,7 +287,7 @@ class PyGTKControl():
         mime_lists = {}
         for mime_list in GUIConfig.mime_lists:
             mime_lists[mime_list] = []
-        tree.get_model().append(('', {}, mime_lists, [], ))
+        tree.get_model().append(('', {}, mime_lists, [], [], {}, ))
 
     def on_add_vhost_menu_item_activate(self, widget):
         '''Adds a new VHost.'''
@@ -281,7 +295,7 @@ class PyGTKControl():
         vhost_lists = {}
         for vhost_list in GUIConfig.vhost_lists:
             vhost_lists[vhost_list[0]] = []
-        tree.get_model().append(('', {}, vhost_lists, [], ))
+        tree.get_model().append(('', {}, vhost_lists, [], [], {}, ))
 
     def on_add_definition_to_mime_menu_item_activate(self, widget):
         '''Adds a definition to currently selected MIME type.'''
@@ -312,12 +326,16 @@ class PyGTKControl():
     def set_up_mime(self, config):
         '''Reads MIME configuration from given config instance.'''
         self.on_new_mime_menu_item_activate()
+        self.mime_custom = config.custom
+        self.mime_custom_attrib = config.custom_attrib
         tree = self.mime_tab[0][1]
         tree.set_up(config.MIME_types)
 
     def set_up_vhost(self, config):
         '''Reads VHost configuration from given config instance.'''
         self.on_new_vhost_menu_item_activate()
+        self.vhost_custom = config.custom
+        self.vhost_custom_attrib = config.custom_attrib
         tree = self.vhost_tab[1]
         tree.set_up(config.VHosts)
 
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
index 039b708..8069a35 100644
--- a/misc/PyGTK_Control/VHostWidgets.py
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -48,7 +48,9 @@ class StreamTreeView(gtk.TreeView):
                 gobject.TYPE_STRING, # stream location
                 gobject.TYPE_PYOBJECT, # list of filters
                 gobject.TYPE_PYOBJECT, # (cycle, cycle active, )
-                gobject.TYPE_BOOLEAN)) # cycle_gzip
+                gobject.TYPE_BOOLEAN, # cycle_gzip
+                gobject.TYPE_PYOBJECT, # stream custom
+                gobject.TYPE_PYOBJECT)) # stream custom attrib
         self.cycle = cycle
         self.cycle_gzip = cycle_gzip
         self.filter_model = filter_tree.get_model()
@@ -115,7 +117,9 @@ class LogTreeView(gtk.TreeView):
         gtk.TreeView.__init__(self, gtk.ListStore(
                 gobject.TYPE_STRING, # log name
                 gobject.TYPE_PYOBJECT, # list of streams
-                gobject.TYPE_PYOBJECT)) # (log_type, log_type enabled, )
+                gobject.TYPE_PYOBJECT, # (log_type, log_type enabled, )
+                gobject.TYPE_PYOBJECT, # log custom
+                gobject.TYPE_PYOBJECT)) # log custom attrib
         self.stream_tree = stream_tree
         self.log_type = log_type
         model = self.get_model()
@@ -153,7 +157,7 @@ class LogTreeView(gtk.TreeView):
             i = model.iter_children(None)
             while i is not None: # iterate over streams
                 row = model[i]
-                streams.append((row[0], row[1], row[2], row[3], ))
+                streams.append((row[0], row[1], row[2], row[3], row[4], 
row[5], ))
                 i = model.iter_next(i)
             row = self.get_model()[self.last_selected]
             row[1] = streams
@@ -182,7 +186,7 @@ class LogTreeView(gtk.TreeView):
         i = model.iter_children(None)
         while i is not None: # iterate over logs
             row = model[i]
-            logs.append((row[0], row[1], row[2], ))
+            logs.append((row[0], row[1], row[2], row[3], row[4], ))
             i = model.iter_next(i)
         return logs
 
@@ -192,7 +196,7 @@ class LogTreeView(gtk.TreeView):
         model = self.get_model()
         model.clear()
         for log in logs:
-            model.append((log[0], log[1], log[2], ))
+            model.append((log[0], log[1], log[2], log[3], log[4], ))
 
 class VHostTreeView(gtk.TreeView):
     def __init__(self):
@@ -200,7 +204,9 @@ class VHostTreeView(gtk.TreeView):
                 gobject.TYPE_STRING, # vhost name
                 gobject.TYPE_PYOBJECT, # vhost single attributes
                 gobject.TYPE_PYOBJECT, # vhost attribute lists
-                gobject.TYPE_PYOBJECT)) # vhost logs
+                gobject.TYPE_PYOBJECT, # vhost logs
+                gobject.TYPE_PYOBJECT, # vhost custom
+                gobject.TYPE_PYOBJECT)) # vhost custom attrib
         model = self.get_model()
         def vhost_edited_handler(cell, path, text, data):
             model = data
@@ -250,17 +256,21 @@ class VHostTreeView(gtk.TreeView):
                         cycle = ''
                     cycle_gzip = stream.get_cycle_gzip()
                     streams.append((stream.get_location(), filters,
-                                    (cycle, cycle_active, ), cycle_gzip, ))
+                                    (cycle, cycle_active, ), cycle_gzip,
+                                    stream.custom, stream.custom_attrib, ))
                 log_type = log.get_type()
                 log_type_enabled = log_type is not None
                 if log_type is None:
                     log_type = ''
                 logs.append((log.get_log_type(), streams,
-                             (log_type, log_type_enabled, ), ))
+                             (log_type, log_type_enabled, ), log.custom,
+                             log.custom_attrib, ))
             model.append((getattr(vhost, 'get_' + GUIConfig.vhost_name)(),
                           attributes,
                           vhost_lists,
-                          logs, ))
+                          logs,
+                          vhost.custom,
+                          vhost.custom_attrib, ))
 
 class VHostTable(gtk.Table):
     def __init__(self, tree):
@@ -362,7 +372,7 @@ class VHostTable(gtk.Table):
         self.log_model = self.log_tree.get_model()
 
         def add_stream(button, model):
-            model.append(('', [], (0, False, ), False, ))
+            model.append(('', [], (0, False, ), False, [], {}, ))
         add_stream_button = gtk.Button('Add stream')
         add_stream_button.connect(
             'clicked', add_stream, stream_tree.get_model())
@@ -468,10 +478,14 @@ class VHostTable(gtk.Table):
                         cycle = None
                     cycle_gzip = stream[3]
                     streams.append(Stream(stream[0], cycle, cycle_gzip, 
stream[1]))
+                    streams[-1].custom = stream[4]
+                    streams[-1].custom_attrib = stream[5]
                 log_type, enabled = log[2]
                 if not enabled:
                     log_type = None
                 logs.append(Log(log[0], streams, log_type))
+                logs[-1].custom = log[3]
+                logs[-1].custom_attrib = log[4]
             vhost = VHost(row[0], logs = logs)
             for attribute in row[1]:
                 text, enabled = row[1][attribute]
@@ -480,6 +494,8 @@ class VHostTable(gtk.Table):
             for vhost_list in row[2]:
                 for entry in row[2][vhost_list]:
                     getattr(vhost, 'add_' + vhost_list)(*entry)
+            vhost.custom = row[4]
+            vhost.custom_attrib = row[5]
             vhosts.append(vhost)
             i = model.iter_next(i)
         return vhosts



commit 89cb5ac1df6b9b6fc4a13db57a7c1c21554aae6e
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 18:48:47 2009 +0200

    Clean up redundant code.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 01e26dc..ce8b533 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -136,137 +136,109 @@ class PyGTKControl():
         table.clear()
         tree.get_model().clear()
 
-    def on_open_config_menu_item_activate(self, widget):
-        '''Open local server configuration file.'''
+    def generic_open(self, dialog, config_type, set_up_method):
+        '''Open configuration file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
-            'Open server configuration file.',
+            dialog,
             buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
                 self.config_path = self.chooser.get_filename()
                 with open(self.config_path) as f:
-                    conf = MyServerConfig.from_string(f.read())
-                self.set_up_config(conf)
+                    conf = config_type.from_string(f.read())
+                set_up_method(conf)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_open_config_menu_item_activate(self, widget):
+        '''Open local server configuration file.'''
+        self.generic_open(
+            'Open server configuration file.',
+            MyServerConfig, self.set_up_config)
+
     def on_open_mime_menu_item_activate(self, widget):
         '''Open local MIME configuration file.'''
-        if self.chooser is not None:
-            self.chooser.destroy()
-        self.chooser = gtk.FileChooserDialog(
+        self.generic_open(
             'Open MIME configuration file.',
-            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                       gtk.STOCK_OPEN, gtk.RESPONSE_OK))
-        def handle_response(widget, response):
-            if response == gtk.RESPONSE_OK:
-                self.mime_path = self.chooser.get_filename()
-                with open(self.mime_path) as f:
-                    conf = MIMETypes.from_string(f.read())
-                self.set_up_mime(conf)
-            self.chooser.destroy()
-        self.chooser.connect('response', handle_response)
-        self.chooser.show()
+            MIMETypes, self.set_up_mime)
 
     def on_open_vhost_menu_item_activate(self, widget):
         '''Open local VHost configuration file.'''
-        if self.chooser is not None:
-            self.chooser.destroy()
-        self.chooser = gtk.FileChooserDialog(
+        self.generic_open(
             'Open VHost configuration file.',
-            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                       gtk.STOCK_OPEN, gtk.RESPONSE_OK))
-        def handle_response(widget, response):
-            if response == gtk.RESPONSE_OK:
-                self.vhost_path = self.chooser.get_filename()
-                with open(self.vhost_path) as f:
-                    conf = VHosts.from_string(f.read())
-                self.set_up_vhost(conf)
-            self.chooser.destroy()
-        self.chooser.connect('response', handle_response)
-        self.chooser.show()
+            VHosts, self.set_up_vhost)
 
-    def on_save_as_config_menu_item_activate(self, widget):
-        '''Save server configuration as local file.'''
+    def generic_save_as(self, dialog, widget, path, save_method):
+        '''Save configuration as file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
-            'Save server configuration file.',
+            dialog,
             action = gtk.FILE_CHOOSER_ACTION_SAVE,
             buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                        gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
-                self.config_path = self.chooser.get_filename()
-                self.on_save_config_menu_item_activate(widget)
+                setattr(self, path, self.chooser.get_filename())
+                save_method(widget)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_save_as_config_menu_item_activate(self, widget):
+        '''Save server configuration as local file.'''
+        self.generic_save_as(
+            'Save server configuration file.',
+            widget, 'config_path',
+            self.on_save_config_menu_item_activate)
+
     def on_save_as_mime_menu_item_activate(self, widget):
         '''Save MIME configuration as local file.'''
-        if self.chooser is not None:
-            self.chooser.destroy()
-        self.chooser = gtk.FileChooserDialog(
+        self.generic_save_as(
             'Save MIME configuration file.',
-            action = gtk.FILE_CHOOSER_ACTION_SAVE,
-            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                       gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
-        def handle_response(widget, response):
-            if response == gtk.RESPONSE_OK:
-                self.mime_path = self.chooser.get_filename()
-                self.on_save_mime_menu_item_activate(widget)
-            self.chooser.destroy()
-        self.chooser.connect('response', handle_response)
-        self.chooser.show()
+            widget, 'mime_path',
+            self.on_save_mime_menu_item_activate)
 
     def on_save_as_vhost_menu_item_activate(self, widget):
         '''Save VHost configuration as local file.'''
-        if self.chooser is not None:
-            self.chooser.destroy()
-        self.chooser = gtk.FileChooserDialog(
+        self.generic_save_as(
             'Save VHost configuration file.',
-            action = gtk.FILE_CHOOSER_ACTION_SAVE,
-            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                       gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
-        def handle_response(widget, response):
-            if response == gtk.RESPONSE_OK:
-                self.vhost_path = self.chooser.get_filename()
-                self.on_save_vhost_menu_item_activate(widget)
-            self.chooser.destroy()
-        self.chooser.connect('response', handle_response)
-        self.chooser.show()
+            widget, 'vhost_path',
+            self.on_save_vhost_menu_item_activate)
 
-    def on_save_config_menu_item_activate(self, widget):
-        '''Save server configuration to file.'''
-        if self.config_path is None:
-            self.on_save_as_config_menu_item_activate(widget)
+    def generic_save(self, widget, path, save_as_method, config_method):
+        '''Save configuration file.'''
+        if getattr(self, path) is None:
+            save_as_method(widget)
         else:
-            config = self.get_current_config()
-            with open(self.config_path, 'w') as f:
+            config = config_method()
+            with open(getattr(self, path), 'w') as f:
                 f.write(str(config))
+        
+    def on_save_config_menu_item_activate(self, widget):
+        '''Save server configuration to file.'''
+        self.generic_save(
+            widget, 'config_path',
+            self.on_save_as_config_menu_item_activate,
+            self.get_current_config)
 
     def on_save_mime_menu_item_activate(self, widget):
         '''Save MIME configuration to file.'''
-        if self.mime_path is None:
-            self.on_save_as_mime_config_menu_item_activate(widget)
-        else:
-            config = self.get_current_mime()
-            with open(self.mime_path, 'w') as f:
-                f.write(str(config))
+        self.generic_save(
+            widget, 'mime_path',
+            self.on_save_as_mime_menu_item_activate,
+            self.get_current_mime)
 
     def on_save_vhost_menu_item_activate(self, widget):
         '''Save VHost configuration to file.'''
-        if self.vhost_path is None:
-            self.on_save_as_vhost_config_menu_item_activate(widget)
-        else:
-            config = self.get_current_vhost()
-            with open(self.vhost_path, 'w') as f:
-                f.write(str(config))
+        self.generic_save(
+            widget, 'vhost_path',
+            self.on_save_as_vhost_menu_item_activate,
+            self.get_current_vhost)
 
     def get_current_config(self):
         '''Returns current server configuration as MyServerConfig instance.'''



commit 7d0405ec49eb938d208dc72730387ce6afb9e7ba
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Aug 12 15:12:34 2009 +0200

    Working keeping unknown things in main config.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 6ef23a2..079dbee 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -22,7 +22,7 @@ from MyServer.pycontrollib.definition import 
DefinitionElement, DefinitionTree
 
 class DefinitionTable(gtk.Table):
     def __init__(self, tree):
-        gtk.Table.__init__(self, 8, 3)
+        gtk.Table.__init__(self, 7, 3)
 
         tree.connect('cursor-changed', self.cursor_changed)
         self.last_selected = None
@@ -36,7 +36,7 @@ class DefinitionTable(gtk.Table):
         value_label = gtk.Label('value:')
         self.value_field = value_entry = gtk.Entry()
         self.value_check_field = value_checkbutton = gtk.CheckButton()
-        value_checkbutton.set_tooltip_text('If active value will be used.')
+        value_checkbutton.set_tooltip_text('If not active value won\'t be 
saved in configuration.')
         value_checkbutton.set_active(True)
 
         self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
@@ -63,6 +63,7 @@ class DefinitionTable(gtk.Table):
 
         add_attribute_button = gtk.Button('add attribute')
         remove_attribute_button = gtk.Button('remove attribute')
+        button_table = gtk.Table(1, 2)
         add_attribute_button.connect(
             'clicked',
             lambda button: attributes_model.append(('', '', )))
@@ -71,8 +72,9 @@ class DefinitionTable(gtk.Table):
             if selected is not None:
                 model.remove(selected)
         remove_attribute_button.connect('clicked', remove_attribute)
-        self.attach(add_attribute_button, 0, 3, 4, 5, yoptions = gtk.FILL)
-        self.attach(remove_attribute_button, 0, 3, 5, 6, yoptions = gtk.FILL)
+        button_table.attach(add_attribute_button, 0, 1, 0, 1)
+        button_table.attach(remove_attribute_button, 1, 2, 0, 1)
+        self.attach(button_table, 0, 3, 4, 5, yoptions = gtk.FILL)
 
         attributes_label = gtk.Label('attributes:')
         attributes_list = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING,
@@ -105,8 +107,8 @@ class DefinitionTable(gtk.Table):
 
         attributes_list.append_column(variable_column)
         attributes_list.append_column(value_column)
-        self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
-        self.attach(attributes_scroll, 0, 3, 7, 8)
+        self.attach(attributes_label, 0, 3, 5, 6, yoptions = gtk.FILL)
+        self.attach(attributes_scroll, 0, 3, 6, 7)
 
     def clear(self):
         '''Clear input widgets.'''
@@ -252,7 +254,7 @@ class DefinitionTreeView(gtk.TreeView):
             i = model.iter_next(i)
 
     def set_up(self, definitions, search):
-        '''Sets up model reading from given definition list. If search in True
+        '''Sets up model reading from given definition list. If search is True
         will substitute alreaty present model data, if it is false will append
         all the definitions.'''
 
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index b2a6c4f..01e26dc 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -44,6 +44,8 @@ class PyGTKControl():
         self.chooser = None # Active file chooser
         # path of currently edited files
         self.config_path = self.mime_path = self.vhost_path = None
+        # remembered unhandled parts of configs
+        self.config_custom = []
         self.controller = None
 
     def on_window_destroy(self, widget):
@@ -110,6 +112,7 @@ class PyGTKControl():
         '''Clears server configuration.'''
         if widget is not None:
             self.config_path = None
+        self.config_custom = []
         table, tree = self.tabs['unknown']
         tree.get_model().clear()
         for tab in self.tabs:
@@ -271,7 +274,9 @@ class PyGTKControl():
         for tab in self.tabs:
             table, tree = self.tabs[tab]
             definitions += table.make_def(tree)
-        return MyServerConfig(definitions)
+        config = MyServerConfig(definitions)
+        config.definitions.custom = self.config_custom
+        return config
 
     def get_current_mime(self):
         '''Returns current MIME configuration as MIMETypes instance.'''
@@ -319,6 +324,7 @@ class PyGTKControl():
     def set_up_config(self, config):
         '''Reads server configuration from given config instance.'''
         self.on_new_config_menu_item_activate()
+        self.config_custom = config.definitions.custom
         tabs = {}
         for tab in self.tabs:
             tabs[tab] = []
diff --git a/misc/py_control_client/MyServer/pycontrollib/config.py 
b/misc/py_control_client/MyServer/pycontrollib/config.py
index ad4e1fb..cdb5643 100644
--- a/misc/py_control_client/MyServer/pycontrollib/config.py
+++ b/misc/py_control_client/MyServer/pycontrollib/config.py
@@ -65,4 +65,6 @@ class MyServerConfig():
         root = etree.Element('MYSERVER')
         for definition in self.definitions.get_definitions():
             root.append(definition.to_lxml_element())
+        for element in self.definitions.custom:
+            root.append(element)
         return root
diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index 327e28b..bce43b9 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -26,7 +26,6 @@ class Definition():
         '''Creates new definition with given name and attributes.'''
         self.set_name(name)
         self.attributes = {}
-        self.custom = [] # list of children not being definitions
         for key, value in attributes.iteritems():
             self.set_attribute(key, value)
 
@@ -137,6 +136,7 @@ class DefinitionTree(Definition):
         self.definitions = []
         for definition in definitions:
             self.add_definition(definition)
+        self.custom = [] # list of children not being definitions
 
     def __eq__(self, other):
         return isinstance(other, DefinitionTree) and \
@@ -208,16 +208,20 @@ class DefinitionList():
     def __init__(self, definitions = []):
         '''Construct new DefinitionList object with given definitions.'''
         self.definitions = []
+        self.custom = []
         for definition in definitions:
             self.add_definition(definition)
 
     def add_definition(self, definition, index = None):
         '''Append definition to current list of definitions, if index is not
         None insert at index-th position.'''
-        if index is None:
-            self.definitions.append(definition)
+        if not isinstance(definition, Definition):
+            self.custom.append(definition)
         else:
-            self.definitions.insert(index, definition)
+            if index is None:
+                self.definitions.append(definition)
+            else:
+                self.definitions.insert(index, definition)
 
     def get_definitions(self):
         '''Get current list of definitions.'''



commit 07c79e454295c046b8e788ea711e4270040a2a7f
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 11 22:08:33 2009 +0200

    Remember unhandled things in streams and logs.

diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index fb39ec2..8752f1b 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -27,6 +27,8 @@ class Stream():
         self.filters = []
         for filter in filters:
             self.add_filter(filter)
+        self.custom = []
+        self.custom_attrib = {}
 
     def __eq__(self, other):
         return isinstance(other, Stream) and \
@@ -92,16 +94,27 @@ class Stream():
         '''Factory to produce stream from lxml.etree.Element object.'''
         if root.tag != 'STREAM':
             raise AttributeError('Expected STREAM tag.')
+        known_attrib = set(['location', 'cycle', 'cycle_gzip'])
+        custom_attrib = {}
+        for key, value in root.attrib.iteritems():
+            if key not in known_attrib:
+                custom_attrib[key] = value
         location = root.get('location')
         cycle = root.get('cycle', None)
         cycle_gzip = root.get('cycle_gzip', None)
         if cycle_gzip is not None:
             cycle_gzip = cycle_gzip.upper() == 'YES'
         filters = []
+        custom = []
         for child in list(root):
             if child.tag == 'FILTER':
                 filters.append(child.text)
-        return Stream(location, cycle, cycle_gzip, filters)
+            else:
+                custom.append(child)
+        stream = Stream(location, cycle, cycle_gzip, filters)
+        stream.custom = custom
+        stream.custom_attrib = custom_attrib
+        return stream
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
@@ -119,6 +132,12 @@ class Stream():
             element = etree.Element('FILTER')
             element.text = filter
             root.append(element)
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+
         return root
 
 class Log():
@@ -129,13 +148,15 @@ class Log():
         self.streams = []
         for stream in streams:
             self.add_stream(stream)
+        self.custom = []
+        self.custom_attrib = {}
 
     def __eq__(self, other):
         return isinstance(other, Log) and \
             self.log_type == other.log_type and \
             self.streams == other.streams and \
             self.type == other.type
-    
+
     def get_type(self):
         '''Get log's type attribute.'''
         return self.type
@@ -169,7 +190,7 @@ class Log():
     def remove_stream(self, index):
         '''Remove stream from index-th position.'''
         self.streams.pop(index)
-    
+
     def get_stream(self, index):
         '''Get index-th stream.'''
         return self.streams[index]
@@ -184,9 +205,23 @@ class Log():
     def from_lxml_element(root):
         '''Factory to produce log from lxml.etree.Element object.'''
         log_type = root.tag
+        custom_attrib = {}
+        known_attrib = set(['type'])
+        for key, value in root.attrib.iteritems():
+            if key not in known_attrib:
+                custom_attrib[key] = value
         type = root.get('type', None)
-        streams = map(Stream.from_lxml_element, list(root))
-        return Log(log_type, streams, type)
+        streams = []
+        custom = []
+        for element in list(root):
+            try:
+                streams.append(Stream.from_lxml_element(element))
+            except AttributeError:
+                custom.append(element)
+        log = Log(log_type, streams, type)
+        log.custom = custom
+        log.custom_attrib = custom_attrib
+        return log
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
@@ -198,4 +233,10 @@ class Log():
             root.set('type', self.type)
         for stream in self.streams:
             root.append(stream.to_lxml_element())
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+
         return root
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/log_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
index 9e01f57..823eacb 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
@@ -382,5 +382,44 @@ class LogTest(unittest.TestCase):
         copy = Log.from_lxml_element(log.to_lxml_element())
         self.assertEqual(log, copy)
 
+class BadMarkupTest(unittest.TestCase):
+    def test_stream(self):
+        text = '''
+<STREAM custom="unknown">
+  abc
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</STREAM>'''
+        stream = Stream.from_string(text)
+        tree = stream.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
+    def test_log(self):
+        text = '''
+<SOMELOG custom="unknown">
+  <STREAM>abc</STREAM>
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</SOMELOG>'''
+        log = Log.from_string(text)
+        tree = log.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 2252324..7564d67 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -145,7 +145,7 @@ class VHost():
     def get_private_key(self):
         '''Get vhost private ssl key.'''
         return self.private_key
-    
+
     def set_private_key(self, private_key):
         '''Set vhost private ssl key.'''
         self.private_key = private_key
@@ -196,7 +196,7 @@ class VHost():
             root.append(element)
         for key, value in self.custom_attrib.iteritems():
             root.set(key, value)
-            
+
         return root
 
     @staticmethod
@@ -248,7 +248,7 @@ class VHost():
         vhost.custom = custom
         vhost.custom_attrib = custom_attrib
         return vhost
-    
+
     @staticmethod
     def from_string(text):
         '''Factory to produce VHost by parsing a string.'''
@@ -276,7 +276,7 @@ class VHosts():
             root.append(element)
         for key, value in self.custom_attrib.iteritems():
             root.set(key, value)
-            
+
         return root
 
     def __str__(self):



commit 498effc0fb50a9d74f390b2e138d329f41e70767
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 11 21:25:15 2009 +0200

    Preserve unknown things in vhosts.

diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index eeed6da..fb39ec2 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -185,10 +185,7 @@ class Log():
         '''Factory to produce log from lxml.etree.Element object.'''
         log_type = root.tag
         type = root.get('type', None)
-        streams = []
-        for child in list(root):
-            if child.tag == 'STREAM':
-                streams.append(Stream.from_lxml_element(child))
+        streams = map(Stream.from_lxml_element, list(root))
         return Log(log_type, streams, type)
 
     def __str__(self):
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index e6d84f6..7338ac6 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -579,5 +579,45 @@ class VHostsTest(unittest.TestCase):
                 '<VHOSTS>{0}</VHOSTS>'.format(str(self.vhost_0))))
         self.assertNotEqual(VHosts.from_string(self.text), [])
 
+class BadMarkupTest(unittest.TestCase):
+    def test_vhost(self):
+        text = '''
+<VHOST custom="unknown">
+  <PORT>123</PORT>
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</VHOST>'''
+        vhost = VHost.from_string(text)
+        tree = vhost.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
+    def test_vhosts(self):
+        text = '''
+<VHOSTS custom="unknown">
+  <VHOST />
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</VHOSTS>'''
+        vhosts = VHosts.from_string(text)
+        tree = vhosts.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        self.assertEqual(1, len(tree.findall('VHOST')))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 54b76f2..2252324 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -42,6 +42,8 @@ class VHost():
             self.add_host(single_host[0], single_host[1])
         self.set_private_key(private_key)
         self.set_certificate(certificate)
+        self.custom = []
+        self.custom_attrib = {}
 
     def __eq__(self, other):
         return isinstance(other, VHost) and self.name == other.name and \
@@ -189,6 +191,12 @@ class VHost():
             root.append(element)
         for log in self.logs:
             root.append(log.to_lxml_element())
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+            
         return root
 
     @staticmethod
@@ -196,6 +204,7 @@ class VHost():
         '''Factory to produce VHost from lxml.etree.Element object.'''
         if root.tag != 'VHOST':
             raise AttributeError('Expected VHOST tag.')
+        custom_attrib = root.attrib
         name = None
         port = None
         protocol = None
@@ -206,6 +215,7 @@ class VHost():
         ip = []
         host = {}
         logs = []
+        custom = []
         for child in list(root):
             if child.tag == 'NAME':
                 name = child.text
@@ -229,9 +239,15 @@ class VHost():
             elif child.tag == 'SSL_CERTIFICATE':
                 certificate = child.text
             else:
-                logs.append(Log.from_lxml_element(child))
-        return VHost(name, port, protocol, doc_root, sys_root, logs, ip, host,
-                     private_key, certificate)
+                try:
+                    logs.append(Log.from_lxml_element(child))
+                except AttributeError:
+                    custom.append(child)
+        vhost = VHost(name, port, protocol, doc_root, sys_root, logs, ip, host,
+                      private_key, certificate)
+        vhost.custom = custom
+        vhost.custom_attrib = custom_attrib
+        return vhost
     
     @staticmethod
     def from_string(text):
@@ -244,6 +260,8 @@ class VHosts():
         '''Create a new instance of VHosts. VHosts attribute is expected to be 
a
         list.'''
         self.VHosts = VHosts
+        self.custom = []
+        self.custom_attrib = {}
 
     def __eq__(self, other):
         return isinstance(other, VHosts) and self.VHosts == other.VHosts
@@ -253,6 +271,12 @@ class VHosts():
         root = etree.Element('VHOSTS')
         for vhost in self.VHosts:
             root.append(vhost.to_lxml_element())
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+            
         return root
 
     def __str__(self):
@@ -263,7 +287,17 @@ class VHosts():
         '''Factory to produce VHosts from lxml.etree.Element object.'''
         if root.tag != 'VHOSTS':
             raise AttributeError('Expected VHOSTS tag.')
-        return VHosts(map(VHost.from_lxml_element, list(root)))
+        vhosts = []
+        custom = []
+        for element in list(root):
+            try:
+                vhosts.append(VHost.from_lxml_element(element))
+            except AttributeError:
+                custom.append(element)
+        vhosts = VHosts(vhosts)
+        vhosts.custom = custom
+        vhosts.custom_attrib = root.attrib
+        return vhosts
 
     @staticmethod
     def from_string(text):



commit 8f8e920949fafb1b328f69a8630f963ab04a4dca
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 11 19:57:46 2009 +0200

    MIME types remember unknown sub-nodes and attributes.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 99b15c3..df978c2 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -37,6 +37,8 @@ class MIMEType():
             self.add_filter(filter)
         self.set_self_executed(self_executed)
         self.definitions = DefinitionList(definitions)
+        self.custom = []
+        self.custom_attrib = {}
 
     def get_mime(self):
         '''Get associated mime type.'''
@@ -114,7 +116,7 @@ class MIMEType():
     def get_definitions(self):
         '''Get all definitions.'''
         return self.definitions.get_definitions()
-    
+
     def get_definition(self, index):
         '''Get definition with given index.'''
         return self.definitions.get_definition(index)
@@ -165,6 +167,12 @@ class MIMEType():
             root.append(element)
         for definition in self.definitions.get_definitions():
             root.append(definition.to_lxml_element())
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+
         return root
 
     def __str__(self):
@@ -179,10 +187,16 @@ class MIMEType():
         handler = root.get('handler', None)
         param = root.get('param', None)
         self_executed = root.get('self', None)
+        known_attrib = set(['mime', 'handler', 'param', 'self'])
+        custom_attrib = {}
+        for key, value in root.attrib.iteritems():
+            if key not in known_attrib:
+                custom_attrib[key] = value
         path = None
         extension = set()
         filters = []
         definitions = []
+        custom = []
         for child in list(root):
             if child.tag == 'PATH':
                 path = child.get('regex')
@@ -192,8 +206,13 @@ class MIMEType():
                 extension.add(child.get('value'))
             elif child.tag == 'DEFINE':
                 definitions.append(Definition.from_lxml_element(child))
-        return MIMEType(mime, handler, param, extension, path, filters,
+            else:
+                custom.append(child)
+        mime = MIMEType(mime, handler, param, extension, path, filters,
                         self_executed, definitions)
+        mime.custom = custom
+        mime.custom_attrib = custom_attrib
+        return mime
 
     @staticmethod
     def from_string(text):
@@ -204,7 +223,9 @@ class MIMEType():
 class MIMETypes():
     def __init__(self, MIME_types = []):
         self.MIME_types = MIME_types
-        
+        self.custom = []
+        self.custom_attrib = {}
+
     def __eq__(self, other):
         return isinstance(other, MIMETypes) and \
             self.MIME_types == other.MIME_types
@@ -214,18 +235,34 @@ class MIMETypes():
         root = etree.Element('MIMES')
         for mime in self.MIME_types:
             root.append(mime.to_lxml_element())
+
+        for element in self.custom:
+            root.append(element)
+        for key, value in self.custom_attrib.iteritems():
+            root.set(key, value)
+
         return root
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
-        
+
     @staticmethod
     def from_lxml_element(root):
         '''Factory to produce MIMETypes from lxml.etree.Element object.'''
         if root.tag != 'MIMES':
             raise AttributeError('Expected MIMES tag.')
-        return MIMETypes(map(MIMEType.from_lxml_element, list(root)))
-    
+        types = []
+        custom = []
+        for element in list(root):
+            try:
+                types.append(MIMEType.from_lxml_element(element))
+            except AttributeError:
+                custom.append(element)
+        mimes = MIMETypes(types)
+        mimes.custom = custom
+        mimes.custom_attrib = root.attrib
+        return mimes
+
     @staticmethod
     def from_string(text):
         '''Factory to produce MIMETypes from parsing a string.'''
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 71d086e..4183577 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -446,6 +446,39 @@ class MIMETypesTest(unittest.TestCase):
         self.assertRaises(AttributeError, MIMETypes.from_string, text)
         self.assertRaises(AttributeError, MIMETypes.from_lxml_element,
                           etree.XML(text))
+
+class BadMarkupTest(unittest.TestCase):
+    def test_mime_types(self):
+        text = '''
+<MIMES custom="unknown">
+  <MIME mime="text/plain" />
+  <MIME mime="text/html" />
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</MIMES>'''
+        mimes = MIMETypes.from_string(text)
+        tree = mimes.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        self.assertEqual(2, len(tree.findall('MIME')))
+        unknown = tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
+    def test_mime_type(self):
+        text = '''
+<MIME mime="text/plain" custom="unknown">
+  <CUSTOM />
+</MIME>'''
+        mime = MIMEType.from_string(text)
+        tree = mime.to_lxml_element()
+        self.assertEqual('unknown', tree.get('custom'))
+        custom = tree.findall('CUSTOM')
+        self.assertEqual(1, len(custom))
         
 if __name__ == '__main__':
     unittest.main()



commit 78d989514db5fe89b11560a684f57d767dbac17c
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 11 15:40:53 2009 +0200

    DefinitionTree keeps unknown tags found in tree.

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index af5dcf0..327e28b 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -26,6 +26,7 @@ class Definition():
         '''Creates new definition with given name and attributes.'''
         self.set_name(name)
         self.attributes = {}
+        self.custom = [] # list of children not being definitions
         for key, value in attributes.iteritems():
             self.set_attribute(key, value)
 
@@ -64,8 +65,8 @@ class Definition():
     def from_lxml_element(root):
         '''Factory to produce definition element or tree from 
lxml.etree.Element
         object.'''
-        if root.tag != 'DEFINE':
-            raise AttributeError('Expected DEFINE tag.')
+        if root.tag != 'DEFINE': # don't touch unknown things
+            return root
         if len(list(root)):
             return DefinitionTree.from_lxml_element(root)
         else:
@@ -153,10 +154,13 @@ class DefinitionTree(Definition):
     def add_definition(self, value, index = None):
         '''Add value to sub-definitions, either at given position, or at the
         end.'''
-        if index is None:
-            self.definitions.append(value)
+        if not isinstance(value, Definition):
+            self.custom.append(value)
         else:
-            self.definitions.insert(index, value)
+            if index is None:
+                self.definitions.append(value)
+            else:
+                self.definitions.insert(index, value)
 
     def remove_definition(self, index):
         '''Remove sub-definition with given index.'''
@@ -186,6 +190,8 @@ class DefinitionTree(Definition):
             root.set('name', self.name)
         for definition in self.definitions:
             root.append(definition.to_lxml_element())
+        for element in self.custom:
+            root.append(element)
         return root
 
     def __str__(self):
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
index 9cb5bcc..669a2a8 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
@@ -76,9 +76,9 @@ class DefinitionTest(unittest.TestCase):
 
     def test_bad_root_tag(self):
         text = '<ERROR name="http.error.file.404" value="404.html" />'
-        self.assertRaises(AttributeError, Definition.from_string, text)
-        self.assertRaises(AttributeError, Definition.from_lxml_element,
-                          etree.XML(text))
+        self.assertEqual('ERROR', Definition.from_string(text).tag)
+        self.assertEqual('ERROR', Definition.from_lxml_element(
+                etree.XML(text)).tag)
 
     def test_equality(self):
         self.assertNotEqual(Definition(), 'different type')
@@ -203,8 +203,8 @@ class DefinitionElementTest(unittest.TestCase):
 
     def test_bad_root_tag(self):
         text = '<ERROR name="http.error.file.404" value="404.html" />'
-        self.assertRaises(AttributeError, Definition.from_string, text)
-        self.assertRaises(AttributeError, Definition.from_lxml_element,
+        self.assertRaises(AttributeError, DefinitionElement.from_string, text)
+        self.assertRaises(AttributeError, DefinitionElement.from_lxml_element,
                           etree.XML(text))
 
     def test_search_by_name(self):
@@ -379,8 +379,8 @@ class DefinitionTreeTest(unittest.TestCase):
     def test_bad_root_tag(self):
         text = '<ERROR name="test" key="value">{0}{1}</ERROR>'.format(
             self.element_0, self.element_1)
-        self.assertRaises(AttributeError, Definition.from_string, text)
-        self.assertRaises(AttributeError, Definition.from_lxml_element,
+        self.assertRaises(AttributeError, DefinitionTree.from_string, text)
+        self.assertRaises(AttributeError, DefinitionTree.from_lxml_element,
                           etree.XML(text))
 
     def test_search_by_name(self):
@@ -456,5 +456,26 @@ class DefinitionListTest(unittest.TestCase):
             copy.add_definition(Definition.from_lxml_element(lxml_definition))
         self.assertEqual(def_list, copy)
 
+class BadMarkupTest(unittest.TestCase):
+    def test_definition_tree(self):
+        text = '''
+<DEFINE>
+  <DEFINE value="x" />
+  <DEFINE vlaue="y" />
+  <UNKNOWN>
+    <CUSTOM />
+  </UNKNOWN>
+</DEFINE>'''
+        definition = Definition.from_string(text)
+        def_tree = definition.to_lxml_element()
+        self.assertEqual(2, len(def_tree.findall('DEFINE')))
+        unknown = def_tree.findall('UNKNOWN')
+        self.assertEqual(1, len(unknown))
+        unknown = unknown[0]
+        custom = list(unknown)
+        self.assertEqual(1, len(custom))
+        custom = custom[0]
+        self.assertEqual('CUSTOM', custom.tag)
+
 if __name__ == '__main__':
     unittest.main()



commit ed000bb9ed508947f31e6f3ad16904430bcfe274
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Aug 11 11:43:55 2009 +0200

    Fixed test executing Makefile.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
index 6ccf7b7..5a784d4 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/Makefile
+++ b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
@@ -21,19 +21,19 @@ PYTHONPATH:=$(shell dirname `pwd`):${PYTHONPATH}
 test: log_test definition_test mimetypes_test vhost_test config_test
 
 log_test:
-       -${PYTHON} log_test.py
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} log_test.py
 
 definition_test:
-       -${PYTHON} definition_test.py
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} definition_test.py
 
 mimetypes_test:
-       -${PYTHON} mimetypes_test.py
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} mimetypes_test.py
 
 vhost_test:
-       -${PYTHON} vhost_test.py
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} vhost_test.py
 
 config_test:
-       -${PYTHON} config_test.py
+       -PYTHONPATH=${PYTHONPATH} ${PYTHON} config_test.py
 
 .PHONY: clean
 clean:



commit cfbe88ed7f1ff39ab79894a272e5164ea2f1a3f6
Merge: 9f48d35 0c8caa0
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Aug 10 21:44:49 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit 9f48d3563c8cb5fa994177aef1e66358f73e95b8
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 31 17:05:18 2009 +0200

    Config files can be put on/got from server using control protocol.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index e367b0e..b2a6c4f 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -77,14 +77,34 @@ class PyGTKControl():
         self.controller = None
 
     def on_get_config_menu_item_activate(self, widget):
-        '''Get config from remote server.'''
+        '''Get server config from remote server.'''
         if self.controller is not None:
-            self.set_up(self.controller.get_server_configuration())
+            self.set_up_config(self.controller.get_server_configuration())
 
+    def on_get_mime_menu_item_activate(self, widget):
+        '''Get MIME config from remote server.'''
+        if self.controller is not None:
+            self.set_up_mime(self.controller.get_MIME_type_configuration())
+            
+    def on_get_vhost_menu_item_activate(self, widget):
+        '''Get VHost config from remote server.'''
+        if self.controller is not None:
+            self.set_up_vhost(self.controller.get_vhost_configuration())
+            
     def on_put_config_menu_item_activate(self, widget):
-        '''Put config on remote server.'''
+        '''Put server config on remote server.'''
         if self.controller is not None:
             self.controller.put_server_configuration(self.get_current_config())
+            
+    def on_put_mime_menu_item_activate(self, widget):
+        '''Put MIME config on remote server.'''
+        if self.controller is not None:
+            
self.controller.put_MIME_type_configuration(self.get_current_mime())
+            
+    def on_put_vhost_menu_item_activate(self, widget):
+        '''Put VHost config on remote server.'''
+        if self.controller is not None:
+            self.controller.put_vhost_configuration(self.get_vhost_config())
 
     def on_new_config_menu_item_activate(self, widget = None):
         '''Clears server configuration.'''



commit 1f9cb30b943f1d1aa820398b4b46c0478c20863c
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 31 15:49:01 2009 +0200

    Updated interface to get/put config from server.

diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index a8a64ec..10af6b3 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -242,29 +242,87 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" 
id="get_config_menu_item">
-                        <property name="label">Get configuration</property>
+                      <widget class="GtkImageMenuItem" id="get_menu_item">
+                        <property name="label">Get</property>
                         <property name="visible">True</property>
                         <property name="use_stock">False</property>
-                        <signal name="activate" 
handler="on_get_config_menu_item_activate"/>
+                        <child>
+                          <widget class="GtkMenu" id="get_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="get_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Get 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_get_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="get_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Get 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_get_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="get_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Get 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_get_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
                         <child internal-child="image">
-                          <widget class="GtkImage" id="get_config_image">
+                          <widget class="GtkImage" id="get_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-go-down</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" 
id="put_config_menu_item">
-                        <property name="label">Put configuration</property>
+                      <widget class="GtkImageMenuItem" id="put_menu_item">
+                        <property name="label">Put</property>
                         <property name="visible">True</property>
                         <property name="use_stock">False</property>
-                        <signal name="activate" 
handler="on_put_config_menu_item_activate"/>
+                        <child>
+                          <widget class="GtkMenu" id="put_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="put_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Put 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_put_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="put_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Put 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_put_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="put_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Put 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_put_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
                         <child internal-child="image">
-                          <widget class="GtkImage" id="put_config_image">
+                          <widget class="GtkImage" id="put_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-redo</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
@@ -291,6 +349,7 @@
                           <widget class="GtkImage" 
id="add_unknown_definition_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-add</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
@@ -317,6 +376,7 @@
                           <widget class="GtkImage" id="add_mime_type_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-add</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
@@ -331,6 +391,7 @@
                           <widget class="GtkImage" 
id="add_definition_to_mime_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-add</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
@@ -357,6 +418,7 @@
                           <widget class="GtkImage" id="add_vhost_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-add</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>
@@ -371,6 +433,7 @@
                           <widget class="GtkImage" id="add_log_to_vhost_image">
                             <property name="visible">True</property>
                             <property name="stock">gtk-add</property>
+                            <property name="icon-size">1</property>
                           </widget>
                         </child>
                       </widget>



commit e7ad47b23cb6a3c74ac0ae0e5cdc609598ded717
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 31 15:07:50 2009 +0200

    Added gui edition of additional Log properties.

diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
index dd826d4..a6f0938 100644
--- a/misc/PyGTK_Control/MIMEWidgets.py
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -66,7 +66,7 @@ class MimeTreeView(gtk.TreeView):
 class MimeTable(gtk.Table):
     def __init__(self, tree, def_tree, def_table):
         gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
-                           3 * len(GUIConfig.mime_lists), 4)
+                           3 * len(GUIConfig.mime_lists), 3)
 
         tree.connect('cursor-changed', self.cursor_changed)
         self.last_selected = None
@@ -81,8 +81,8 @@ class MimeTable(gtk.Table):
             check = gtk.CheckButton()
             self.attributes[attribute] = (entry, check, )
             self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
-            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            self.attach(entry, 1, 2, i, i + 1, yoptions = gtk.FILL)
+            self.attach(check, 2, 3, i, i + 1, gtk.FILL, gtk.FILL)
             i += 1
         self.mime_lists = {}
         for mime_list in GUIConfig.mime_lists:
@@ -116,10 +116,10 @@ class MimeTable(gtk.Table):
             remove_button.connect('clicked', remove_from_tree, tree)
 
             self.mime_lists[mime_list] = tree_model
-            self.attach(tree_scroll, 0, 2, i, i + 3)
-            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
-            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            self.attach(tree_scroll, 1, 2, i, i + 3)
+            self.attach(add_button, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            self.attach(remove_button, 0, 1, i + 1, i + 2, yoptions = gtk.FILL)
+            self.attach(gtk.Label(), 0, 1, i + 2, i + 3)
             i += 3
             
     def clear(self):
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 1d38ce4..e367b0e 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -380,9 +380,9 @@ class PyGTKControl():
 
         panels = gtk.HPaned()
         tree = MimeTreeView()
-        panels.pack1(tree.scroll, True, False)
+        panels.pack1(tree.scroll, False, False)
         table = MimeTable(tree, def_tree, def_table)
-        panels.pack2(table, False, False)
+        panels.pack2(table, True, False)
 
         vpanels.pack1(panels)
         
@@ -396,9 +396,9 @@ class PyGTKControl():
         '''Reads vhost options from file and prepares GUI.'''
         panels = gtk.HPaned()
         tree = VHostTreeView()
-        panels.pack1(tree.scroll, True, False)
+        panels.pack1(tree.scroll, False, False)
         table = VHostTable(tree)
-        panels.pack2(table, False, False)
+        panels.pack2(table.scroll, True, False)
 
         self.vhost_tab = (table, tree, )
         self.widgets.get_widget('notebook').append_page(
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
index 43f982d..039b708 100644
--- a/misc/PyGTK_Control/VHostWidgets.py
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -43,10 +43,14 @@ class FilterTreeView(gtk.TreeView):
         self.scroll.add(self)
 
 class StreamTreeView(gtk.TreeView):
-    def __init__(self, filter_tree):
+    def __init__(self, filter_tree, cycle, cycle_gzip):
         gtk.TreeView.__init__(self, gtk.ListStore(
                 gobject.TYPE_STRING, # stream location
-                gobject.TYPE_PYOBJECT)) # list of filters
+                gobject.TYPE_PYOBJECT, # list of filters
+                gobject.TYPE_PYOBJECT, # (cycle, cycle active, )
+                gobject.TYPE_BOOLEAN)) # cycle_gzip
+        self.cycle = cycle
+        self.cycle_gzip = cycle_gzip
         self.filter_model = filter_tree.get_model()
         model = self.get_model()
         def edited_handler(cell, path, text, model):
@@ -58,6 +62,7 @@ class StreamTreeView(gtk.TreeView):
         column.pack_start(renderer)
         column.add_attribute(renderer, 'text', 0)
         self.append_column(column)
+
         self.scroll = gtk.ScrolledWindow()
         self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
         self.scroll.set_shadow_type(gtk.SHADOW_OUT)
@@ -70,6 +75,10 @@ class StreamTreeView(gtk.TreeView):
     def clear(self):
         self.filter_model.clear()
         self.last_selected = None
+        entry, check = self.cycle
+        entry.set_value(0)
+        check.set_active(False)
+        self.cycle_gzip.set_active(False)
 
     def save_changed(self):
         model = self.filter_model
@@ -81,6 +90,9 @@ class StreamTreeView(gtk.TreeView):
                 i = model.iter_next(i)
             row = self.get_model()[self.last_selected]
             row[1] = filters
+            entry, check = self.cycle
+            row[2] = (entry.get_value(), check.get_active(), )
+            row[3] = self.cycle_gzip.get_active()
 
     def cursor_changed(self, tree):
         self.save_changed()
@@ -93,13 +105,19 @@ class StreamTreeView(gtk.TreeView):
         row = tree.get_model()[current]
         for element in row[1]: # add all filters
             model.append((element, ))
+        entry, check = self.cycle
+        entry.set_value(row[2][0])
+        check.set_active(row[2][1])
+        self.cycle_gzip.set_active(row[3])
 
 class LogTreeView(gtk.TreeView):
-    def __init__(self, stream_tree):
+    def __init__(self, stream_tree, log_type):
         gtk.TreeView.__init__(self, gtk.ListStore(
                 gobject.TYPE_STRING, # log name
-                gobject.TYPE_PYOBJECT)) # list of streams
+                gobject.TYPE_PYOBJECT, # list of streams
+                gobject.TYPE_PYOBJECT)) # (log_type, log_type enabled, )
         self.stream_tree = stream_tree
+        self.log_type = log_type
         model = self.get_model()
         def edited_handler(cell, path, text, model):
             model[path][0] = text
@@ -123,6 +141,9 @@ class LogTreeView(gtk.TreeView):
         self.stream_tree.clear()
         self.last_selected = None
         self.stream_tree.get_model().clear()
+        entry, check = self.log_type
+        entry.set_text('')
+        check.set_active(False)
         
     def save_changed(self):
         self.stream_tree.save_changed()
@@ -132,10 +153,12 @@ class LogTreeView(gtk.TreeView):
             i = model.iter_children(None)
             while i is not None: # iterate over streams
                 row = model[i]
-                streams.append((row[0], row[1], ))
+                streams.append((row[0], row[1], row[2], row[3], ))
                 i = model.iter_next(i)
             row = self.get_model()[self.last_selected]
             row[1] = streams
+            entry, check = self.log_type
+            row[2] = (entry.get_text(), check.get_active(), )
 
     def cursor_changed(self, tree):
         self.save_changed()
@@ -148,6 +171,9 @@ class LogTreeView(gtk.TreeView):
         row = tree.get_model()[current]
         for stream in row[1]:
             model.append(stream)
+        entry, check = self.log_type
+        entry.set_text(row[2][0])
+        check.set_active(row[2][1])
 
     def get_logs(self):
         self.save_changed()
@@ -156,7 +182,7 @@ class LogTreeView(gtk.TreeView):
         i = model.iter_children(None)
         while i is not None: # iterate over logs
             row = model[i]
-            logs.append((row[0], row[1], ))
+            logs.append((row[0], row[1], row[2], ))
             i = model.iter_next(i)
         return logs
 
@@ -166,7 +192,7 @@ class LogTreeView(gtk.TreeView):
         model = self.get_model()
         model.clear()
         for log in logs:
-            model.append((log[0], log[1], ))
+            model.append((log[0], log[1], log[2], ))
 
 class VHostTreeView(gtk.TreeView):
     def __init__(self):
@@ -218,8 +244,19 @@ class VHostTreeView(gtk.TreeView):
                 streams = []
                 for stream in log.get_streams():
                     filters = stream.get_filters()
-                    streams.append((stream.get_location(), filters, ))
-                logs.append((log.get_log_type(), streams, ))
+                    cycle = stream.get_cycle()
+                    cycle_active = cycle is not None
+                    if cycle is None:
+                        cycle = ''
+                    cycle_gzip = stream.get_cycle_gzip()
+                    streams.append((stream.get_location(), filters,
+                                    (cycle, cycle_active, ), cycle_gzip, ))
+                log_type = log.get_type()
+                log_type_enabled = log_type is not None
+                if log_type is None:
+                    log_type = ''
+                logs.append((log.get_log_type(), streams,
+                             (log_type, log_type_enabled, ), ))
             model.append((getattr(vhost, 'get_' + GUIConfig.vhost_name)(),
                           attributes,
                           vhost_lists,
@@ -228,7 +265,7 @@ class VHostTreeView(gtk.TreeView):
 class VHostTable(gtk.Table):
     def __init__(self, tree):
         gtk.Table.__init__(self, len(GUIConfig.vhost_attributes) +
-                           3 * len(GUIConfig.vhost_lists) + 3, 4)
+                           3 * len(GUIConfig.vhost_lists) + 6, 3)
 
         tree.connect('cursor-changed', self.cursor_changed)
         self.last_selected = None
@@ -241,8 +278,8 @@ class VHostTable(gtk.Table):
             check = gtk.CheckButton()
             self.attributes[attribute] = (entry, check, )
             self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
-            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            self.attach(entry, 1, 2, i, i + 1, yoptions = gtk.FILL)
+            self.attach(check, 2, 3, i, i + 1, gtk.FILL, gtk.FILL)
             i += 1
             
         self.vhost_lists = {}
@@ -290,20 +327,42 @@ class VHostTable(gtk.Table):
             remove_button.connect('clicked', remove_from_tree, tree)
 
             self.vhost_lists[name] = tree_model
-            self.attach(tree_scroll, 0, 2, i, i + 3)
-            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
-            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            self.attach(tree_scroll, 1, 2, i, i + 3)
+            self.attach(add_button, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            self.attach(remove_button, 0, 1, i + 1, i + 2, yoptions = gtk.FILL)
+            self.attach(gtk.Label(), 0, 1, i + 2, i + 3)
             i += 3
 
         # Add logs
+        log_type_label = gtk.Label('log type')
+        log_type_entry = gtk.Entry()
+        log_type_check = gtk.CheckButton()
+        self.attach(log_type_label, 0, 1, i + 3, i + 4, yoptions = gtk.FILL)
+        self.attach(log_type_entry, 1, 2, i + 3, i + 4, yoptions = gtk.FILL)
+        self.attach(log_type_check, 2, 3, i + 3, i + 4, gtk.FILL, gtk.FILL)
+
+        cycle_label = gtk.Label('cycle')
+        cycle_entry = gtk.SpinButton(gtk.Adjustment(upper = 2 ** 32,
+                                                    step_incr = 1))
+        cycle_check = gtk.CheckButton()
+        cycle_gzip_label = gtk.Label('cycle gzip')
+        cycle_gzip_check = gtk.CheckButton()
+        self.attach(cycle_label, 0, 1, i + 4, i + 5, yoptions = gtk.FILL)
+        self.attach(cycle_entry, 1, 2, i + 4, i + 5, yoptions = gtk.FILL)
+        self.attach(cycle_check, 2, 3, i + 4, i + 5, gtk.FILL, gtk.FILL)
+        self.attach(cycle_gzip_label, 0, 1, i + 5, i + 6, yoptions = gtk.FILL)
+        self.attach(cycle_gzip_check, 2, 3, i + 5, i + 6, gtk.FILL, gtk.FILL)
+        
         filter_tree = FilterTreeView()
-        stream_tree = StreamTreeView(filter_tree)
-        self.log_tree = LogTreeView(stream_tree)
+        stream_tree = StreamTreeView(filter_tree,
+                                     (cycle_entry, cycle_check, ),
+                                     cycle_gzip_check)
+        self.log_tree = LogTreeView(stream_tree, (log_type_entry,
+                                                  log_type_check, ))
         self.log_model = self.log_tree.get_model()
 
         def add_stream(button, model):
-            model.append(('', [], ))
+            model.append(('', [], (0, False, ), False, ))
         add_stream_button = gtk.Button('Add stream')
         add_stream_button.connect(
             'clicked', add_stream, stream_tree.get_model())
@@ -328,14 +387,18 @@ class VHostTable(gtk.Table):
         button_grid.attach(remove_stream_button, 0, 1, 1, 2)
         button_grid.attach(remove_filter_button, 1, 2, 1, 2)
 
-        self.attach(self.log_tree.scroll, 0, 2, i, i + 3)
-        self.attach(button_grid, 2, 3, i, i + 1, yoptions = gtk.FILL)
-        self.attach(stream_tree.scroll, 2, 3, i + 1, i + 2)
-        self.attach(filter_tree.scroll, 2, 3, i + 2, i + 3)
+        self.attach(self.log_tree.scroll, 0, 1, i, i + 3)
+        self.attach(button_grid, 1, 2, i, i + 1, yoptions = gtk.FILL)
+        self.attach(stream_tree.scroll, 1, 2, i + 1, i + 2)
+        self.attach(filter_tree.scroll, 1, 2, i + 2, i + 3)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.add_with_viewport(self)
 
     def add_log(self):
         '''Add a log to currently selected VHost.'''
-        self.log_model.append(('', [], ))
+        self.log_model.append(('', [], ('', False, )))
             
     def clear(self):
         '''Clear input widgets.'''
@@ -400,8 +463,15 @@ class VHostTable(gtk.Table):
             for log in row[3]:
                 streams = []
                 for stream in log[1]:
-                    streams.append(Stream(stream[0], filters = stream[1]))
-                logs.append(Log(log[0], streams = streams))
+                    cycle, enabled = stream[2]
+                    if not enabled:
+                        cycle = None
+                    cycle_gzip = stream[3]
+                    streams.append(Stream(stream[0], cycle, cycle_gzip, 
stream[1]))
+                log_type, enabled = log[2]
+                if not enabled:
+                    log_type = None
+                logs.append(Log(log[0], streams, log_type))
             vhost = VHost(row[0], logs = logs)
             for attribute in row[1]:
                 text, enabled = row[1][attribute]



commit 9c64c9d44ce1b960917d4bd307935db995ea14d0
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 29 15:08:43 2009 +0200

    Open, save of VHosts.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 49103d8..1d38ce4 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -24,6 +24,7 @@ import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
 from MyServer.pycontrollib.mimetypes import MIMETypes
 from MyServer.pycontrollib.controller import Controller
+from MyServer.pycontrollib.vhost import VHosts
 from AboutWindow import About
 from ConnectionWindow import Connection
 from DefinitionWidgets import DefinitionTable, DefinitionTreeView
@@ -104,6 +105,14 @@ class PyGTKControl():
         table.clear()
         tree.get_model().clear()
 
+    def on_new_vhost_menu_item_activate(self, widget = None):
+        '''Clears VHost configuration.'''
+        if widget is not None:
+            self.vhost_path = None
+        table, tree = self.vhost_tab
+        table.clear()
+        tree.get_model().clear()
+
     def on_open_config_menu_item_activate(self, widget):
         '''Open local server configuration file.'''
         if self.chooser is not None:
@@ -140,6 +149,24 @@ class PyGTKControl():
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_open_vhost_menu_item_activate(self, widget):
+        '''Open local VHost configuration file.'''
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Open VHost configuration file.',
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                self.vhost_path = self.chooser.get_filename()
+                with open(self.vhost_path) as f:
+                    conf = VHosts.from_string(f.read())
+                self.set_up_vhost(conf)
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
     def on_save_as_config_menu_item_activate(self, widget):
         '''Save server configuration as local file.'''
         if self.chooser is not None:
@@ -157,7 +184,7 @@ class PyGTKControl():
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
-    def on_save_as_mime_config_menu_item_activate(self, widget):
+    def on_save_as_mime_menu_item_activate(self, widget):
         '''Save MIME configuration as local file.'''
         if self.chooser is not None:
             self.chooser.destroy()
@@ -174,6 +201,23 @@ class PyGTKControl():
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_save_as_vhost_menu_item_activate(self, widget):
+        '''Save VHost configuration as local file.'''
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Save VHost configuration file.',
+            action = gtk.FILE_CHOOSER_ACTION_SAVE,
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                self.vhost_path = self.chooser.get_filename()
+                self.on_save_vhost_menu_item_activate(widget)
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
     def on_save_config_menu_item_activate(self, widget):
         '''Save server configuration to file.'''
         if self.config_path is None:
@@ -192,6 +236,15 @@ class PyGTKControl():
             with open(self.mime_path, 'w') as f:
                 f.write(str(config))
 
+    def on_save_vhost_menu_item_activate(self, widget):
+        '''Save VHost configuration to file.'''
+        if self.vhost_path is None:
+            self.on_save_as_vhost_config_menu_item_activate(widget)
+        else:
+            config = self.get_current_vhost()
+            with open(self.vhost_path, 'w') as f:
+                f.write(str(config))
+
     def get_current_config(self):
         '''Returns current server configuration as MyServerConfig instance.'''
         definitions = []
@@ -201,11 +254,17 @@ class PyGTKControl():
         return MyServerConfig(definitions)
 
     def get_current_mime(self):
-        '''Returns current mime configuration as MIMETypes instance.'''
+        '''Returns current MIME configuration as MIMETypes instance.'''
         table, tree = self.mime_tab[0]
         mimes = table.make_def(tree)
         return MIMETypes(mimes)
 
+    def get_current_vhost(self):
+        '''Returns current VHost configuration as VHosts instance.'''
+        table, tree = self.vhost_tab
+        mimes = table.make_def(tree)
+        return VHosts(mimes)
+
     def on_add_unknown_definition_menu_item_activate(self, widget):
         '''Adds a new definition to unknown tab.'''
         table, tree = self.tabs['unknown']
@@ -258,6 +317,12 @@ class PyGTKControl():
         tree = self.mime_tab[0][1]
         tree.set_up(config.MIME_types)
 
+    def set_up_vhost(self, config):
+        '''Reads VHost configuration from given config instance.'''
+        self.on_new_vhost_menu_item_activate()
+        tree = self.vhost_tab[1]
+        tree.set_up(config.VHosts)
+
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''
 
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
index a7012e6..43f982d 100644
--- a/misc/PyGTK_Control/VHostWidgets.py
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -19,6 +19,8 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 import gtk
 import gobject
 import GUIConfig
+from MyServer.pycontrollib.log import Log, Stream
+from MyServer.pycontrollib.vhost import VHost
 
 class FilterTreeView(gtk.TreeView):
     def __init__(self):
@@ -150,11 +152,11 @@ class LogTreeView(gtk.TreeView):
     def get_logs(self):
         self.save_changed()
         model = self.get_model()
-        logs = {}
+        logs = []
         i = model.iter_children(None)
         while i is not None: # iterate over logs
             row = model[i]
-            logs[row[0]] = row[1]
+            logs.append((row[0], row[1], ))
             i = model.iter_next(i)
         return logs
 
@@ -164,7 +166,7 @@ class LogTreeView(gtk.TreeView):
         model = self.get_model()
         model.clear()
         for log in logs:
-            model.append((log, logs[log], ))
+            model.append((log[0], log[1], ))
 
 class VHostTreeView(gtk.TreeView):
     def __init__(self):
@@ -191,22 +193,37 @@ class VHostTreeView(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
-    # def set_up(self, MIME_types):
-    #     '''Fill model with data provided as list of MIME types.'''
-    #     model = self.get_model()
-    #     for mime in MIME_types:
-    #         attributes = {}
-    #         for attribute in GUIConfig.mime_attributes:
-    #             a = getattr(mime, 'get_' + attribute)()
-    #             if a is not None:
-    #                 attributes[attribute] = (a, True, )
-    #         mime_lists = {}
-    #         for mime_list in GUIConfig.mime_lists:
-    #             mime_lists[mime_list] = getattr(mime, 'get_' + mime_list)()
-    #         model.append((getattr(mime, 'get_' + GUIConfig.mime_name)(),
-    #                       attributes,
-    #                       mime_lists,
-    #                       mime.get_definitions(), ))
+    def set_up(self, vhosts):
+        '''Fill model with data provided as list of Vhosts.'''
+        model = self.get_model()
+        for vhost in vhosts:
+            attributes = {}
+            for attribute in GUIConfig.vhost_attributes:
+                a = getattr(vhost, 'get_' + attribute)()
+                if a is not None:
+                    attributes[attribute] = (str(a), True, )
+            vhost_lists = {}
+            for vhost_list in GUIConfig.vhost_lists:
+                vhost_list = vhost_list[0]
+                l = getattr(vhost, 'get_' + vhost_list)()
+                m = []
+                for element in l:
+                    if isinstance(l, dict):
+                        m.append((element, l[element], ))
+                    else:
+                        m.append((element, ))
+                vhost_lists[vhost_list] = m
+            logs = []
+            for log in vhost.get_logs():
+                streams = []
+                for stream in log.get_streams():
+                    filters = stream.get_filters()
+                    streams.append((stream.get_location(), filters, ))
+                logs.append((log.get_log_type(), streams, ))
+            model.append((getattr(vhost, 'get_' + GUIConfig.vhost_name)(),
+                          attributes,
+                          vhost_lists,
+                          logs, ))
 
 class VHostTable(gtk.Table):
     def __init__(self, tree):
@@ -327,6 +344,8 @@ class VHostTable(gtk.Table):
             check.set_active(False)
         for model in self.vhost_lists.itervalues():
             model.clear()
+        self.log_tree.clear()
+        self.log_tree.get_model().clear()
         self.last_selected = None
 
     def save_changed(self, tree):
@@ -369,22 +388,28 @@ class VHostTable(gtk.Table):
                 model.append(element)
         self.log_tree.set_logs(row[3])
 
-    # def make_def(self, tree):
-    #     '''Export all data as list of MIME types.'''
-    #     self.save_changed(tree)
-    #     model = tree.get_model()
-    #     mimes = []
-    #     i = model.iter_children(None)
-    #     while i is not None: # iterate over MIME types
-    #         row = model[i]
-    #         mime = MIMEType(row[0], definitions = row[3])
-    #         for attribute in row[1]:
-    #             text, enabled = row[1][attribute]
-    #             if enabled:
-    #                 getattr(mime, 'set_' + attribute)(text)
-    #         for mime_list in row[2]:
-    #             for entry in row[2][mime_list]:
-    #                 getattr(mime, 'add_' + mime_list[:-1])(entry)
-    #         mimes.append(mime)
-    #         i = model.iter_next(i)
-    #     return mimes
+    def make_def(self, tree):
+        '''Export all data as list of VHosts.'''
+        self.save_changed(tree)
+        model = tree.get_model()
+        vhosts = []
+        i = model.iter_children(None)
+        while i is not None: # iterate over VHosts
+            row = model[i]
+            logs = []
+            for log in row[3]:
+                streams = []
+                for stream in log[1]:
+                    streams.append(Stream(stream[0], filters = stream[1]))
+                logs.append(Log(log[0], streams = streams))
+            vhost = VHost(row[0], logs = logs)
+            for attribute in row[1]:
+                text, enabled = row[1][attribute]
+                if enabled:
+                    getattr(vhost, 'set_' + attribute)(text)
+            for vhost_list in row[2]:
+                for entry in row[2][vhost_list]:
+                    getattr(vhost, 'add_' + vhost_list)(*entry)
+            vhosts.append(vhost)
+            i = model.iter_next(i)
+        return vhosts



commit 91f91d95e12396a71f64c29af06172741bd6aa72
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 28 00:35:24 2009 +0200

    Full support for logs.

diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
index 93dac9b..a7012e6 100644
--- a/misc/PyGTK_Control/VHostWidgets.py
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -20,19 +20,17 @@ import gtk
 import gobject
 import GUIConfig
 
-class LogTreeView(gtk.TreeView):
+class FilterTreeView(gtk.TreeView):
     def __init__(self):
         gtk.TreeView.__init__(self, gtk.ListStore(
-                gobject.TYPE_STRING, # log name
-                gobject.TYPE_PYOBJECT)) # list of streams
+                gobject.TYPE_STRING))
         model = self.get_model()
-        self.last_selected = None
         def edited_handler(cell, path, text, model):
             model[path][0] = text
         renderer = gtk.CellRendererText()
         renderer.set_property('editable', True)
         renderer.connect('edited', edited_handler, model)
-        column = gtk.TreeViewColumn('Log')
+        column = gtk.TreeViewColumn('Filter')
         column.pack_start(renderer)
         column.add_attribute(renderer, 'text', 0)
         self.append_column(column)
@@ -42,51 +40,115 @@ class LogTreeView(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
-        self.stream_tree = stream_tree = gtk.TreeView(
-            gtk.ListStore(gobject.TYPE_STRING))
-        self.stream_model = stream_model = stream_tree.get_model()
-        def stream_edited_handler(cell, path, text, model):
+class StreamTreeView(gtk.TreeView):
+    def __init__(self, filter_tree):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gobject.TYPE_STRING, # stream location
+                gobject.TYPE_PYOBJECT)) # list of filters
+        self.filter_model = filter_tree.get_model()
+        model = self.get_model()
+        def edited_handler(cell, path, text, model):
             model[path][0] = text
         renderer = gtk.CellRendererText()
         renderer.set_property('editable', True)
-        renderer.connect('edited', stream_edited_handler, stream_model)
+        renderer.connect('edited', edited_handler, model)
         column = gtk.TreeViewColumn('Stream')
         column.pack_start(renderer)
         column.add_attribute(renderer, 'text', 0)
-        stream_tree.append_column(column)
-        self.stream_scroll = gtk.ScrolledWindow()
-        self.stream_scroll.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
-        self.stream_scroll.set_shadow_type(gtk.SHADOW_OUT)
-        self.stream_scroll.set_border_width(5)
-        self.stream_scroll.add(stream_tree)
+        self.append_column(column)
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
 
+        self.last_selected = None
         self.connect('cursor-changed', self.cursor_changed)
 
-    def save_changed(self, tree):
-        model = self.stream_model
+    def clear(self):
+        self.filter_model.clear()
+        self.last_selected = None
+
+    def save_changed(self):
+        model = self.filter_model
+        if self.last_selected is not None:
+            filters = []
+            i = model.iter_children(None)
+            while i is not None: # iterate over filters
+                filters.append(model[i][0])
+                i = model.iter_next(i)
+            row = self.get_model()[self.last_selected]
+            row[1] = filters
+
+    def cursor_changed(self, tree):
+        self.save_changed()
+
+        self.clear()
+        model = self.filter_model
+
+        self.last_selected = current = \
+            tree.get_selection().get_selected()[1]
+        row = tree.get_model()[current]
+        for element in row[1]: # add all filters
+            model.append((element, ))
+
+class LogTreeView(gtk.TreeView):
+    def __init__(self, stream_tree):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gobject.TYPE_STRING, # log name
+                gobject.TYPE_PYOBJECT)) # list of streams
+        self.stream_tree = stream_tree
+        model = self.get_model()
+        def edited_handler(cell, path, text, model):
+            model[path][0] = text
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        renderer.connect('edited', edited_handler, model)
+        column = gtk.TreeViewColumn('Log')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 0)
+        self.append_column(column)
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+        
+        self.last_selected = None
+        self.connect('cursor-changed', self.cursor_changed)
+
+    def clear(self):
+        self.stream_tree.clear()
+        self.last_selected = None
+        self.stream_tree.get_model().clear()
+        
+    def save_changed(self):
+        self.stream_tree.save_changed()
+        model = self.stream_tree.get_model()
         if self.last_selected is not None:
             streams = []
             i = model.iter_children(None)
             while i is not None: # iterate over streams
-                streams.append(model[i][0])
+                row = model[i]
+                streams.append((row[0], row[1], ))
                 i = model.iter_next(i)
-            row = tree.get_model()[self.last_selected]
+            row = self.get_model()[self.last_selected]
             row[1] = streams
 
     def cursor_changed(self, tree):
-        self.save_changed(tree)
+        self.save_changed()
 
-        model = self.stream_model
-        model.clear()
+        self.clear()
+        model = self.stream_tree.get_model()
 
         self.last_selected = current = \
             tree.get_selection().get_selected()[1]
         row = tree.get_model()[current]
         for stream in row[1]:
-            model.append((stream, ))
+            model.append(stream)
 
     def get_logs(self):
-        self.save_changed(self)
+        self.save_changed()
         model = self.get_model()
         logs = {}
         i = model.iter_children(None)
@@ -97,8 +159,7 @@ class LogTreeView(gtk.TreeView):
         return logs
 
     def set_logs(self, logs):
-        self.stream_model.clear()
-        self.stream_tree.get_selection().unselect_all()
+        self.clear()
         self.last_selected = None
         model = self.get_model()
         model.clear()
@@ -219,25 +280,41 @@ class VHostTable(gtk.Table):
             i += 3
 
         # Add logs
-        self.log_tree = log_tree = LogTreeView()
-        self.log_model = log_tree.get_model()
+        filter_tree = FilterTreeView()
+        stream_tree = StreamTreeView(filter_tree)
+        self.log_tree = LogTreeView(stream_tree)
+        self.log_model = self.log_tree.get_model()
 
         def add_stream(button, model):
+            model.append(('', [], ))
+        add_stream_button = gtk.Button('Add stream')
+        add_stream_button.connect(
+            'clicked', add_stream, stream_tree.get_model())
+        def add_filter(button, model):
             model.append(('', ))
-        add_button = gtk.Button('Add')
-        add_button.connect('clicked', add_stream, log_tree.stream_model)
+        add_filter_button = gtk.Button('Add filter')
+        add_filter_button.connect(
+            'clicked', add_filter, filter_tree.get_model())
 
-        def remove_stream(button, tree):
+        def remove_selected(button, tree):
             model, selected = tree.get_selection().get_selected()
             if selected is not None:
                 model.remove(selected)
-        remove_button = gtk.Button('Remove')
-        remove_button.connect('clicked', remove_stream, log_tree.stream_tree)
+        remove_stream_button = gtk.Button('Remove stream')
+        remove_stream_button.connect('clicked', remove_selected, stream_tree)
+        remove_filter_button = gtk.Button('Remove filter')
+        remove_filter_button.connect('clicked', remove_selected, filter_tree)
+
+        button_grid = gtk.Table(2, 2)
+        button_grid.attach(add_stream_button, 0, 1, 0, 1)
+        button_grid.attach(add_filter_button, 1, 2, 0, 1)
+        button_grid.attach(remove_stream_button, 0, 1, 1, 2)
+        button_grid.attach(remove_filter_button, 1, 2, 1, 2)
 
-        self.attach(log_tree.scroll, 0, 2, i, i + 3)
-        self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
-        self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
-        self.attach(log_tree.stream_scroll, 2, 3, i + 2, i + 3)
+        self.attach(self.log_tree.scroll, 0, 2, i, i + 3)
+        self.attach(button_grid, 2, 3, i, i + 1, yoptions = gtk.FILL)
+        self.attach(stream_tree.scroll, 2, 3, i + 1, i + 2)
+        self.attach(filter_tree.scroll, 2, 3, i + 2, i + 3)
 
     def add_log(self):
         '''Add a log to currently selected VHost.'''



commit c91a8e42dbbdd0bd4ab595f9e3b94c0829d0a93c
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 27 01:21:12 2009 +0200

    Added VHosts.
    
    Logs lack filters, cycle and cycle_gzip. VHosts can't be saved or read
    from file.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 5b6d185..6ef23a2 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -38,15 +38,7 @@ class DefinitionTable(gtk.Table):
         self.value_check_field = value_checkbutton = gtk.CheckButton()
         value_checkbutton.set_tooltip_text('If active value will be used.')
         value_checkbutton.set_active(True)
-        def toggle_value(button, tree):
-            # don't allow value for definition with children
-            model, selected = tree.get_selection().get_selected()
-            if selected is not None and model.iter_children(selected) is not 
None:
-                button.set_active(False)
-                return
-            active = button.get_active()
-            self.value_field.set_editable(active)
-        value_checkbutton.connect('toggled', toggle_value, tree)
+
         self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
@@ -57,7 +49,6 @@ class DefinitionTable(gtk.Table):
             if selected is not None:
                 model.append(selected, ('', '', False, True, '', False, {}, ))
                 self.value_check_field.set_active(False) # auto disable value
-                self.value_field.set_editable(False)
         add_definition_button.connect('clicked', add_sub_definition)
         self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
 
diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
index c51f5f6..fc14f6e 100644
--- a/misc/PyGTK_Control/GUIConfig.py
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -16,6 +16,8 @@ You should have received a copy of the GNU General Public 
License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
+from gobject import TYPE_STRING, TYPE_BOOLEAN
+
 options = {}
 
 # Control
@@ -72,3 +74,18 @@ tabs = ['server', 'control', 'ftp', 'http', 'log_color']
 mime_name = 'mime'
 mime_attributes = ['handler', 'self_executed', 'param', 'path']
 mime_lists = ['extensions', 'filters']
+
+# VHosts
+vhost_name = 'name'
+vhost_attributes = ['port', 'protocol', 'doc_root', 'sys_root', 'private_key', 
'certificate']
+vhost_lists = [
+    ('ip', (
+            ('ip', TYPE_STRING, '', ),
+            ),
+     ),
+     ('host', (
+            ('host name', TYPE_STRING, '', ),
+            ('use regexp', TYPE_BOOLEAN, False, ),
+            ),
+      ),
+    ]
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 104b430..49103d8 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -28,6 +28,7 @@ from AboutWindow import About
 from ConnectionWindow import Connection
 from DefinitionWidgets import DefinitionTable, DefinitionTreeView
 from MIMEWidgets import MimeTable, MimeTreeView
+from VHostWidgets import VHostTable, VHostTreeView
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -38,6 +39,7 @@ class PyGTKControl():
         self.widgets.signal_autoconnect(self)
         self.construct_options()
         self.construct_mime()
+        self.construct_vhosts()
         self.chooser = None # Active file chooser
         # path of currently edited files
         self.config_path = self.mime_path = self.vhost_path = None
@@ -217,11 +219,24 @@ class PyGTKControl():
             mime_lists[mime_list] = []
         tree.get_model().append(('', {}, mime_lists, [], ))
 
+    def on_add_vhost_menu_item_activate(self, widget):
+        '''Adds a new VHost.'''
+        table, tree = self.vhost_tab
+        vhost_lists = {}
+        for vhost_list in GUIConfig.vhost_lists:
+            vhost_lists[vhost_list[0]] = []
+        tree.get_model().append(('', {}, vhost_lists, [], ))
+
     def on_add_definition_to_mime_menu_item_activate(self, widget):
         '''Adds a definition to currently selected MIME type.'''
         table, tree = self.mime_tab[1]
         tree.get_model().append(None, ('', '', False, True, '', False, {}, ))
 
+    def on_add_log_to_vhost_menu_item_activate(self, widget):
+        '''Adds a log to currently selected VHost.'''
+        table, tree = self.vhost_tab
+        table.add_log()
+
     def set_up_config(self, config):
         '''Reads server configuration from given config instance.'''
         self.on_new_config_menu_item_activate()
@@ -312,5 +327,19 @@ class PyGTKControl():
 
         self.widgets.get_widget('notebook').show_all()
 
+    def construct_vhosts(self):
+        '''Reads vhost options from file and prepares GUI.'''
+        panels = gtk.HPaned()
+        tree = VHostTreeView()
+        panels.pack1(tree.scroll, True, False)
+        table = VHostTable(tree)
+        panels.pack2(table, False, False)
+
+        self.vhost_tab = (table, tree, )
+        self.widgets.get_widget('notebook').append_page(
+            panels, gtk.Label('VHosts'))
+
+        self.widgets.get_widget('notebook').show_all()
+
 PyGTKControl()
 gtk.main()
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 44c8e5f..a8a64ec 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -340,6 +340,46 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkMenuItem" id="vhost_menu_item">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_VHosts</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="vhost_menu">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="add_vhost_menu_item">
+                        <property name="label">Add VHost</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_add_vhost_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="add_vhost_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-add</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="add_log_to_vhost_menu_item">
+                        <property name="label">Add log to VHost</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_add_log_to_vhost_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="add_log_to_vhost_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-add</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>
diff --git a/misc/PyGTK_Control/VHostWidgets.py 
b/misc/PyGTK_Control/VHostWidgets.py
new file mode 100644
index 0000000..93dac9b
--- /dev/null
+++ b/misc/PyGTK_Control/VHostWidgets.py
@@ -0,0 +1,313 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gobject
+import GUIConfig
+
+class LogTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gobject.TYPE_STRING, # log name
+                gobject.TYPE_PYOBJECT)) # list of streams
+        model = self.get_model()
+        self.last_selected = None
+        def edited_handler(cell, path, text, model):
+            model[path][0] = text
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        renderer.connect('edited', edited_handler, model)
+        column = gtk.TreeViewColumn('Log')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 0)
+        self.append_column(column)
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+        self.stream_tree = stream_tree = gtk.TreeView(
+            gtk.ListStore(gobject.TYPE_STRING))
+        self.stream_model = stream_model = stream_tree.get_model()
+        def stream_edited_handler(cell, path, text, model):
+            model[path][0] = text
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        renderer.connect('edited', stream_edited_handler, stream_model)
+        column = gtk.TreeViewColumn('Stream')
+        column.pack_start(renderer)
+        column.add_attribute(renderer, 'text', 0)
+        stream_tree.append_column(column)
+        self.stream_scroll = gtk.ScrolledWindow()
+        self.stream_scroll.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
+        self.stream_scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.stream_scroll.set_border_width(5)
+        self.stream_scroll.add(stream_tree)
+
+        self.connect('cursor-changed', self.cursor_changed)
+
+    def save_changed(self, tree):
+        model = self.stream_model
+        if self.last_selected is not None:
+            streams = []
+            i = model.iter_children(None)
+            while i is not None: # iterate over streams
+                streams.append(model[i][0])
+                i = model.iter_next(i)
+            row = tree.get_model()[self.last_selected]
+            row[1] = streams
+
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+
+        model = self.stream_model
+        model.clear()
+
+        self.last_selected = current = \
+            tree.get_selection().get_selected()[1]
+        row = tree.get_model()[current]
+        for stream in row[1]:
+            model.append((stream, ))
+
+    def get_logs(self):
+        self.save_changed(self)
+        model = self.get_model()
+        logs = {}
+        i = model.iter_children(None)
+        while i is not None: # iterate over logs
+            row = model[i]
+            logs[row[0]] = row[1]
+            i = model.iter_next(i)
+        return logs
+
+    def set_logs(self, logs):
+        self.stream_model.clear()
+        self.stream_tree.get_selection().unselect_all()
+        self.last_selected = None
+        model = self.get_model()
+        model.clear()
+        for log in logs:
+            model.append((log, logs[log], ))
+
+class VHostTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gobject.TYPE_STRING, # vhost name
+                gobject.TYPE_PYOBJECT, # vhost single attributes
+                gobject.TYPE_PYOBJECT, # vhost attribute lists
+                gobject.TYPE_PYOBJECT)) # vhost logs
+        model = self.get_model()
+        def vhost_edited_handler(cell, path, text, data):
+            model = data
+            model[path][0] = text
+        vhost_renderer = gtk.CellRendererText()
+        vhost_renderer.set_property('editable', True)
+        vhost_renderer.connect('edited', vhost_edited_handler, model)
+        vhost_column = gtk.TreeViewColumn('VHost')
+        vhost_column.pack_start(vhost_renderer)
+        vhost_column.add_attribute(vhost_renderer, 'text', 0)
+        self.append_column(vhost_column)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+    # def set_up(self, MIME_types):
+    #     '''Fill model with data provided as list of MIME types.'''
+    #     model = self.get_model()
+    #     for mime in MIME_types:
+    #         attributes = {}
+    #         for attribute in GUIConfig.mime_attributes:
+    #             a = getattr(mime, 'get_' + attribute)()
+    #             if a is not None:
+    #                 attributes[attribute] = (a, True, )
+    #         mime_lists = {}
+    #         for mime_list in GUIConfig.mime_lists:
+    #             mime_lists[mime_list] = getattr(mime, 'get_' + mime_list)()
+    #         model.append((getattr(mime, 'get_' + GUIConfig.mime_name)(),
+    #                       attributes,
+    #                       mime_lists,
+    #                       mime.get_definitions(), ))
+
+class VHostTable(gtk.Table):
+    def __init__(self, tree):
+        gtk.Table.__init__(self, len(GUIConfig.vhost_attributes) +
+                           3 * len(GUIConfig.vhost_lists) + 3, 4)
+
+        tree.connect('cursor-changed', self.cursor_changed)
+        self.last_selected = None
+
+        self.attributes = {}
+        i = 0
+        for attribute in GUIConfig.vhost_attributes: # Add single attributes
+            label = gtk.Label(attribute)
+            entry = gtk.Entry()
+            check = gtk.CheckButton()
+            self.attributes[attribute] = (entry, check, )
+            self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            i += 1
+            
+        self.vhost_lists = {}
+        for vhost_list in GUIConfig.vhost_lists: # Add attribute lists
+            name = vhost_list[0]
+            column_names = []
+            columns = []
+            defaults = []
+            for element in vhost_list[1]:
+                column_names.append(element[0])
+                columns.append(element[1])
+                defaults.append(element[2])
+            tree = gtk.TreeView(gtk.ListStore(*(columns)))
+            tree_model = tree.get_model()
+            def tree_edited_handler(cell, path, text, data):
+                model, col_index = data
+                model[path][col_index] = text
+            col_index = 0
+            for column in column_names:
+                tree_renderer = gtk.CellRendererText()
+                tree_renderer.set_property('editable', True)
+                tree_renderer.connect('edited', tree_edited_handler,
+                                      (tree_model, col_index, ))
+                tree_column = gtk.TreeViewColumn(column)
+                tree_column.pack_start(tree_renderer)
+                tree_column.add_attribute(tree_renderer, 'text', col_index)
+                tree.append_column(tree_column)
+                col_index += 1
+            tree_scroll = gtk.ScrolledWindow()
+            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+            tree_scroll.set_border_width(5)
+            tree_scroll.add(tree)
+
+            def add_to_tree(button, data):
+                model, defaults = data
+                model.append(defaults)
+            add_button = gtk.Button('Add')
+            add_button.connect('clicked', add_to_tree, (tree_model, defaults, 
))
+            def remove_from_tree(button, tree):
+                model, selected = tree.get_selection().get_selected()
+                if selected is not None:
+                    model.remove(selected)
+            remove_button = gtk.Button('Remove')
+            remove_button.connect('clicked', remove_from_tree, tree)
+
+            self.vhost_lists[name] = tree_model
+            self.attach(tree_scroll, 0, 2, i, i + 3)
+            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
+            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            i += 3
+
+        # Add logs
+        self.log_tree = log_tree = LogTreeView()
+        self.log_model = log_tree.get_model()
+
+        def add_stream(button, model):
+            model.append(('', ))
+        add_button = gtk.Button('Add')
+        add_button.connect('clicked', add_stream, log_tree.stream_model)
+
+        def remove_stream(button, tree):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None:
+                model.remove(selected)
+        remove_button = gtk.Button('Remove')
+        remove_button.connect('clicked', remove_stream, log_tree.stream_tree)
+
+        self.attach(log_tree.scroll, 0, 2, i, i + 3)
+        self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
+        self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
+        self.attach(log_tree.stream_scroll, 2, 3, i + 2, i + 3)
+
+    def add_log(self):
+        '''Add a log to currently selected VHost.'''
+        self.log_model.append(('', [], ))
+            
+    def clear(self):
+        '''Clear input widgets.'''
+        for entry, check in self.attributes.itervalues():
+            entry.set_text('')
+            check.set_active(False)
+        for model in self.vhost_lists.itervalues():
+            model.clear()
+        self.last_selected = None
+
+    def save_changed(self, tree):
+        '''Save data from input widgets to model.'''
+        if self.last_selected is not None:
+            attributes = {}
+            for attribute in self.attributes:
+                entry, check = self.attributes[attribute]
+                attributes[attribute] = (entry.get_text(), check.get_active(), 
)
+            vhost_lists = {}
+            for vhost_list in GUIConfig.vhost_lists:
+                container = vhost_lists[vhost_list[0]] = []
+                model = self.vhost_lists[vhost_list[0]]
+                i = model.iter_children(None)
+                while i is not None: # iterate over list elements
+                    row = []
+                    for counter in xrange(len(vhost_list[1])):
+                        row.append(model[i][counter])
+                    container.append(tuple(row))
+                    i = model.iter_next(i)
+            row = tree.get_model()[self.last_selected]
+            row[1] = attributes
+            row[2] = vhost_lists
+            row[3] = self.log_tree.get_logs()
+
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+
+        self.clear()
+
+        self.last_selected = current = tree.get_selection().get_selected()[1]
+        row = tree.get_model()[current]
+        for attribute in row[1]:
+            entry, check = self.attributes[attribute]
+            entry.set_text(row[1][attribute][0])
+            check.set_active(row[1][attribute][1])
+        for vhost_list in row[2]:
+            model = self.vhost_lists[vhost_list]
+            for element in row[2][vhost_list]:
+                model.append(element)
+        self.log_tree.set_logs(row[3])
+
+    # def make_def(self, tree):
+    #     '''Export all data as list of MIME types.'''
+    #     self.save_changed(tree)
+    #     model = tree.get_model()
+    #     mimes = []
+    #     i = model.iter_children(None)
+    #     while i is not None: # iterate over MIME types
+    #         row = model[i]
+    #         mime = MIMEType(row[0], definitions = row[3])
+    #         for attribute in row[1]:
+    #             text, enabled = row[1][attribute]
+    #             if enabled:
+    #                 getattr(mime, 'set_' + attribute)(text)
+    #         for mime_list in row[2]:
+    #             for entry in row[2][mime_list]:
+    #                 getattr(mime, 'add_' + mime_list[:-1])(entry)
+    #         mimes.append(mime)
+    #         i = model.iter_next(i)
+    #     return mimes



commit 65b8080af3f230a44eaea44557de323ff52a1a77
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 25 23:06:04 2009 +0200

    New/open/save for MIME types.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 1bd8e66..5b6d185 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -152,7 +152,7 @@ class DefinitionTable(gtk.Table):
         self.value_field.set_text(row[4])
         self.value_check_field.set_active(row[5])
         for attribute in row[6]:
-            self.attributes_field.append((attribute, row[5][attribute], ))
+            self.attributes_field.append((attribute, row[6][attribute], ))
 
     def get_selected(self, tree):
         '''Get iterator of currently selected row.'''
@@ -261,7 +261,7 @@ class DefinitionTreeView(gtk.TreeView):
             i = model.iter_next(i)
 
     def set_up(self, definitions, search):
-        '''Sets up model reading from given config instance. If search in True
+        '''Sets up model reading from given definition list. If search in True
         will substitute alreaty present model data, if it is false will append
         all the definitions.'''
 
diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
index d8980fa..dd826d4 100644
--- a/misc/PyGTK_Control/MIMEWidgets.py
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -19,6 +19,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 import gtk
 import gobject
 import GUIConfig
+from MyServer.pycontrollib.mimetypes import MIMEType
 
 class MimeTreeView(gtk.TreeView):
     def __init__(self):
@@ -45,6 +46,23 @@ class MimeTreeView(gtk.TreeView):
         self.scroll.set_border_width(5)
         self.scroll.add(self)
 
+    def set_up(self, MIME_types):
+        '''Fill model with data provided as list of MIME types.'''
+        model = self.get_model()
+        for mime in MIME_types:
+            attributes = {}
+            for attribute in GUIConfig.mime_attributes:
+                a = getattr(mime, 'get_' + attribute)()
+                if a is not None:
+                    attributes[attribute] = (a, True, )
+            mime_lists = {}
+            for mime_list in GUIConfig.mime_lists:
+                mime_lists[mime_list] = getattr(mime, 'get_' + mime_list)()
+            model.append((getattr(mime, 'get_' + GUIConfig.mime_name)(),
+                          attributes,
+                          mime_lists,
+                          mime.get_definitions(), ))
+
 class MimeTable(gtk.Table):
     def __init__(self, tree, def_tree, def_table):
         gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
@@ -113,6 +131,7 @@ class MimeTable(gtk.Table):
             model.clear()
         self.def_table.clear()
         self.def_tree.get_model().clear()
+        self.last_selected = None
 
     def save_changed(self, tree):
         '''Save data from input widgets to model.'''
@@ -150,3 +169,23 @@ class MimeTable(gtk.Table):
             for element in row[2][mime_list]:
                 model.append((element, ))
         self.def_tree.set_up(row[3], False)
+
+    def make_def(self, tree):
+        '''Export all data as list of MIME types.'''
+        self.save_changed(tree)
+        model = tree.get_model()
+        mimes = []
+        i = model.iter_children(None)
+        while i is not None: # iterate over MIME types
+            row = model[i]
+            mime = MIMEType(row[0], definitions = row[3])
+            for attribute in row[1]:
+                text, enabled = row[1][attribute]
+                if enabled:
+                    getattr(mime, 'set_' + attribute)(text)
+            for mime_list in row[2]:
+                for entry in row[2][mime_list]:
+                    getattr(mime, 'add_' + mime_list[:-1])(entry)
+            mimes.append(mime)
+            i = model.iter_next(i)
+        return mimes
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index b0c1d4e..104b430 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -22,6 +22,7 @@ import gtk.glade
 import gobject
 import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
+from MyServer.pycontrollib.mimetypes import MIMETypes
 from MyServer.pycontrollib.controller import Controller
 from AboutWindow import About
 from ConnectionWindow import Connection
@@ -38,7 +39,8 @@ class PyGTKControl():
         self.construct_options()
         self.construct_mime()
         self.chooser = None # Active file chooser
-        self.config_path = None # path of currently edited file
+        # path of currently edited files
+        self.config_path = self.mime_path = self.vhost_path = None
         self.controller = None
 
     def on_window_destroy(self, widget):
@@ -82,7 +84,7 @@ class PyGTKControl():
             self.controller.put_server_configuration(self.get_current_config())
 
     def on_new_config_menu_item_activate(self, widget = None):
-        '''Clears configuration.'''
+        '''Clears server configuration.'''
         if widget is not None:
             self.config_path = None
         table, tree = self.tabs['unknown']
@@ -92,12 +94,20 @@ class PyGTKControl():
             table.clear()
             tree.make_clear()
 
+    def on_new_mime_menu_item_activate(self, widget = None):
+        '''Clears MIME configuration.'''
+        if widget is not None:
+            self.mime_path = None
+        table, tree = self.mime_tab[0]
+        table.clear()
+        tree.get_model().clear()
+
     def on_open_config_menu_item_activate(self, widget):
-        '''Open local configuration file.'''
+        '''Open local server configuration file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
-            'Open configuration file.',
+            'Open server configuration file.',
             buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
         def handle_response(widget, response):
@@ -105,17 +115,35 @@ class PyGTKControl():
                 self.config_path = self.chooser.get_filename()
                 with open(self.config_path) as f:
                     conf = MyServerConfig.from_string(f.read())
-                self.set_up(conf)
+                self.set_up_config(conf)
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
+    def on_open_mime_menu_item_activate(self, widget):
+        '''Open local MIME configuration file.'''
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Open MIME configuration file.',
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                self.mime_path = self.chooser.get_filename()
+                with open(self.mime_path) as f:
+                    conf = MIMETypes.from_string(f.read())
+                self.set_up_mime(conf)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
     def on_save_as_config_menu_item_activate(self, widget):
-        '''Save configuration as local file.'''
+        '''Save server configuration as local file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
-            'Save configuration file.',
+            'Save server configuration file.',
             action = gtk.FILE_CHOOSER_ACTION_SAVE,
             buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                        gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
@@ -127,8 +155,25 @@ class PyGTKControl():
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_save_as_mime_config_menu_item_activate(self, widget):
+        '''Save MIME configuration as local file.'''
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Save MIME configuration file.',
+            action = gtk.FILE_CHOOSER_ACTION_SAVE,
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                self.mime_path = self.chooser.get_filename()
+                self.on_save_mime_menu_item_activate(widget)
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
     def on_save_config_menu_item_activate(self, widget):
-        '''Save configuration to file.'''
+        '''Save server configuration to file.'''
         if self.config_path is None:
             self.on_save_as_config_menu_item_activate(widget)
         else:
@@ -136,14 +181,29 @@ class PyGTKControl():
             with open(self.config_path, 'w') as f:
                 f.write(str(config))
 
+    def on_save_mime_menu_item_activate(self, widget):
+        '''Save MIME configuration to file.'''
+        if self.mime_path is None:
+            self.on_save_as_mime_config_menu_item_activate(widget)
+        else:
+            config = self.get_current_mime()
+            with open(self.mime_path, 'w') as f:
+                f.write(str(config))
+
     def get_current_config(self):
-        '''Returns current configuration as MyServerConfig instance.'''
+        '''Returns current server configuration as MyServerConfig instance.'''
         definitions = []
         for tab in self.tabs:
             table, tree = self.tabs[tab]
             definitions += table.make_def(tree)
         return MyServerConfig(definitions)
 
+    def get_current_mime(self):
+        '''Returns current mime configuration as MIMETypes instance.'''
+        table, tree = self.mime_tab[0]
+        mimes = table.make_def(tree)
+        return MIMETypes(mimes)
+
     def on_add_unknown_definition_menu_item_activate(self, widget):
         '''Adds a new definition to unknown tab.'''
         table, tree = self.tabs['unknown']
@@ -162,8 +222,8 @@ class PyGTKControl():
         table, tree = self.mime_tab[1]
         tree.get_model().append(None, ('', '', False, True, '', False, {}, ))
 
-    def set_up(self, config):
-        '''Reads configuration from given config instance.'''
+    def set_up_config(self, config):
+        '''Reads server configuration from given config instance.'''
         self.on_new_config_menu_item_activate()
         tabs = {}
         for tab in self.tabs:
@@ -177,6 +237,12 @@ class PyGTKControl():
             tree = self.tabs[tab][1]
             tree.set_up(tabs[tab], tab != 'unknown')
 
+    def set_up_mime(self, config):
+        '''Reads MIME configuration from given config instance.'''
+        self.on_new_mime_menu_item_activate()
+        tree = self.mime_tab[0][1]
+        tree.set_up(config.MIME_types)
+
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''
 



commit 7d48adfa5075a1ca7b36ce866fb06fbff6a60152
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 25 23:02:51 2009 +0200

    MIMEType self_executed is string not (was bool).

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index c805338..99b15c3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -156,7 +156,7 @@ class MIMEType():
         if self.param is not None:
             root.set('param', self.param)
         if self.self_executed is not None:
-            root.set('self', 'YES' if self.self_executed else 'NO')
+            root.set('self', self.self_executed)
         if self.path is not None:
             root.append(make_element('PATH', 'regex', self.path))
         for element in map(make_extension_element, self.extensions):
@@ -179,8 +179,6 @@ class MIMEType():
         handler = root.get('handler', None)
         param = root.get('param', None)
         self_executed = root.get('self', None)
-        if self_executed is not None:
-            self_executed = self_executed.upper() == 'YES'
         path = None
         extension = set()
         filters = []
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 09f0351..71d086e 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -44,9 +44,9 @@ class MIMETypeTest(unittest.TestCase):
         MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
                  '^/cgi-bin/.*$', ['gzip'])
         MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
-                 '^/cgi-bin/.*$', ['gzip'], False)
+                 '^/cgi-bin/.*$', ['gzip'], 'NO')
         MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
-                 '^/cgi-bin/.*$', ['gzip'], False, self.definitions)
+                 '^/cgi-bin/.*$', ['gzip'], 'NO', self.definitions)
 
     def test_mime(self):
         mime = MIMEType('text/plain', 'SEND')
@@ -116,12 +116,12 @@ class MIMETypeTest(unittest.TestCase):
     def test_self_executed(self):
         mime = MIMEType('text/plain', 'SEND')
         self.assertEqual(None, mime.get_self_executed())
-        mime.set_self_executed(True)
-        self.assertEqual(True, mime.get_self_executed())
+        mime.set_self_executed('YES')
+        self.assertEqual('YES', mime.get_self_executed())
         mime.set_self_executed(None)
         self.assertEqual(None, mime.get_self_executed())
-        mime = MIMEType('text/plain', 'SEND', self_executed = True)
-        self.assertEqual(True, mime.get_self_executed())
+        mime = MIMEType('text/plain', 'SEND', self_executed = 'YES')
+        self.assertEqual('YES', mime.get_self_executed())
         
     def test_definitions(self):
         mime = MIMEType('text/plain', 'SEND')
@@ -138,58 +138,58 @@ class MIMETypeTest(unittest.TestCase):
         
     def test_equality(self):
         self.assertEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  set(['py']), '^/.*$', ['gzip'], 'NO',
                                   self.definitions),
                          MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  set(['py']), '^/.*$', ['gzip'], 'NO',
                                   self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  set(['py']), '^/.*$', ['gzip'], 'NO',
                                   self.definitions),
                             MIMEType('other', 'CGI', '/usr/bin/python',
-                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'SEND', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python26',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['pyc']), '^/.*$', ['gzip'], False,
+                                     set(['pyc']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/other/.*$', ['gzip'], 
False,
+                                     set(['py']), '^/other/.*$', ['gzip'], 
'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['bzip2'], False,
+                                     set(['py']), '^/.*$', ['bzip2'], 'NO',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], True,
+                                     set(['py']), '^/.*$', ['gzip'], 'YES',
                                      self.definitions))
         self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      self.definitions),
                             MIMEType('text/plain', 'CGI', '/usr/bin/python',
-                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     set(['py']), '^/.*$', ['gzip'], 'NO',
                                      []))
         self.assertNotEqual(MIMEType('text/plain', 'SEND'), 'other type')
 
@@ -229,7 +229,7 @@ class MIMETypeTest(unittest.TestCase):
     def test_from_string_self_executed(self):
         text = '<MIME mime="text/plain" handler="SEND" self="YES" />'
         mime = MIMEType.from_string(text)
-        right = MIMEType('text/plain', 'SEND', self_executed = True)
+        right = MIMEType('text/plain', 'SEND', self_executed = 'YES')
         self.assertEqual(mime, right)
 
     def test_from_string_definitions(self):
@@ -249,7 +249,7 @@ class MIMETypeTest(unittest.TestCase):
 </MIME>'''.format('\n'.join(map(str, self.definitions)))
         mime = MIMEType.from_string(text)
         right = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
-                         ['gzip', 'bzip2'], True, self.definitions)
+                         ['gzip', 'bzip2'], 'YES', self.definitions)
         self.assertEqual(mime, right)
 
     def test_from_lxml(self):
@@ -288,7 +288,7 @@ class MIMETypeTest(unittest.TestCase):
     def test_from_lxml_self_executed(self):
         text = '<MIME mime="text/plain" handler="SEND" self="YES" />'
         mime = MIMEType.from_lxml_element(etree.XML(text))
-        right = MIMEType('text/plain', 'SEND', self_executed = True)
+        right = MIMEType('text/plain', 'SEND', self_executed = 'YES')
         self.assertEqual(mime, right)
 
     def test_from_lxml_definitions(self):
@@ -308,7 +308,7 @@ class MIMETypeTest(unittest.TestCase):
 </MIME>'''.format('\n'.join(map(str, self.definitions)))
         mime = MIMEType.from_lxml_element(etree.XML(text))
         right = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
-                         ['gzip', 'bzip2'], True, self.definitions)
+                         ['gzip', 'bzip2'], 'YES', self.definitions)
         self.assertEqual(mime, right)
 
     def test_bad_root_tag(self):
@@ -343,7 +343,7 @@ class MIMETypeTest(unittest.TestCase):
         self.assertEqual(mime, copy)
 
     def test_to_string_self_executed(self):
-        mime = MIMEType('text/plain', 'SEND', self_executed = True)
+        mime = MIMEType('text/plain', 'SEND', self_executed = 'YES')
         copy = MIMEType.from_string(str(mime))
         self.assertEqual(mime, copy)
 
@@ -354,7 +354,7 @@ class MIMETypeTest(unittest.TestCase):
         
     def test_to_string_full(self):
         mime = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
-                        ['gzip', 'bzip2'], True, self.definitions)
+                        ['gzip', 'bzip2'], 'YES', self.definitions)
         copy = MIMEType.from_string(str(mime))
         self.assertEqual(mime, copy)
 
@@ -384,7 +384,7 @@ class MIMETypeTest(unittest.TestCase):
         self.assertEqual(mime, copy)
 
     def test_to_lxml_self_executed(self):
-        mime = MIMEType('text/plain', 'SEND', self_executed = True)
+        mime = MIMEType('text/plain', 'SEND', self_executed = 'YES')
         copy = MIMEType.from_lxml_element(mime.to_lxml_element())
         self.assertEqual(mime, copy)
 
@@ -395,13 +395,13 @@ class MIMETypeTest(unittest.TestCase):
         
     def test_to_lxml_full(self):
         mime = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
-                        ['gzip', 'bzip2'], True, self.definitions)
+                        ['gzip', 'bzip2'], 'YES', self.definitions)
         copy = MIMEType.from_lxml_element(mime.to_lxml_element())
         self.assertEqual(mime, copy)
 
 class MIMETypesTest(unittest.TestCase):
     def setUp(self):
-        self.mime_0 = MIMEType('text/html', 'FASTCGI', '', self_executed = 
True,
+        self.mime_0 = MIMEType('text/html', 'FASTCGI', '', self_executed = 
'YES',
                                extensions = ['fcgi'])
         self.mime_1 = MIMEType('text/plain', 'SEND', '',
                                extensions = ['asc', 'c', 'cc', 'f', 'f90', 
'h', 'hh'])



commit 36415041ce2c79cde231ad3008563427ad32f8b1
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 25 22:46:01 2009 +0200

    MIMEType doesn't need handler.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 43a13cb..c805338 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -20,8 +20,9 @@ from definition import Definition, DefinitionList
 from lxml import etree
 
 class MIMEType():
-    def __init__(self, mime, handler, param = None, extensions = [], path = 
None,
-                 filters = [], self_executed = None, definitions = []):
+    def __init__(self, mime, handler = None, param = None, extensions = [],
+                 path = None, filters = [], self_executed = None,
+                 definitions = []):
         '''Creates new MIMEType with specified attributes. extension, filter 
and
         definitions are expected to be iterable.'''
         self.set_mime(mime)
@@ -53,8 +54,6 @@ class MIMEType():
 
     def set_handler(self, handler):
         '''Set associated handler.'''
-        if handler is None:
-            raise AttributeError('handler is required and can\'t be None')
         self.handler = handler
 
     def get_param(self):
@@ -152,7 +151,8 @@ class MIMEType():
             return make_element('FILTER', 'value', filter)
         root = etree.Element('MIME')
         root.set('mime', self.mime)
-        root.set('handler', self.handler)
+        if self.handler is not None:
+            root.set('handler', self.handler)
         if self.param is not None:
             root.set('param', self.param)
         if self.self_executed is not None:
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 017368d..09f0351 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -35,6 +35,7 @@ class MIMETypeTest(unittest.TestCase):
                  DefinitionElement(attributes = {'value': 'index.htm'})]))
 
     def test_creation(self):
+        MIMEType('text/plain')
         MIMEType('text/plain', 'CGI')
         MIMEType('text/plain', 'CGI', '/usr/bin/python')
         MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']))
@@ -56,12 +57,16 @@ class MIMETypeTest(unittest.TestCase):
         self.assertRaises(AttributeError, MIMEType, None, 'SEND')
         
     def test_handler(self):
+        mime = MIMEType('text/plain',)
+        self.assertEqual(None, mime.get_handler())
+        mime.set_handler('SEND')
+        self.assertEqual('SEND', mime.get_handler())
+        mime.set_handler(None)
+        self.assertEqual(None, mime.get_handler())
         mime = MIMEType('text/plain', 'SEND')
         self.assertEqual('SEND', mime.get_handler())
-        mime.set_handler('SOME HANDLER')
-        self.assertEqual('SOME HANDLER', mime.get_handler())
-        self.assertRaises(AttributeError, mime.set_handler, None)
-        self.assertRaises(AttributeError, MIMEType, 'text/plain', None)
+        mime = MIMEType('text/plain', handler = 'SEND')
+        self.assertEqual('SEND', mime.get_handler())
 
     def test_param(self):
         mime = MIMEType('text/plain', 'SEND')



commit 2796f1923fdc16bf6be4da8d4dcc7539104bf7d9
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 25 17:42:45 2009 +0200

    Renamed methods to match new names from glade.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index fca487a..b0c1d4e 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -38,7 +38,7 @@ class PyGTKControl():
         self.construct_options()
         self.construct_mime()
         self.chooser = None # Active file chooser
-        self.path = None # path of currently edited file
+        self.config_path = None # path of currently edited file
         self.controller = None
 
     def on_window_destroy(self, widget):
@@ -81,7 +81,18 @@ class PyGTKControl():
         if self.controller is not None:
             self.controller.put_server_configuration(self.get_current_config())
 
-    def on_open_menu_item_activate(self, widget):
+    def on_new_config_menu_item_activate(self, widget = None):
+        '''Clears configuration.'''
+        if widget is not None:
+            self.config_path = None
+        table, tree = self.tabs['unknown']
+        tree.get_model().clear()
+        for tab in self.tabs:
+            table, tree = self.tabs[tab]
+            table.clear()
+            tree.make_clear()
+
+    def on_open_config_menu_item_activate(self, widget):
         '''Open local configuration file.'''
         if self.chooser is not None:
             self.chooser.destroy()
@@ -91,15 +102,15 @@ class PyGTKControl():
                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
-                self.path = self.chooser.get_filename()
-                with open(self.path) as f:
+                self.config_path = self.chooser.get_filename()
+                with open(self.config_path) as f:
                     conf = MyServerConfig.from_string(f.read())
                 self.set_up(conf)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
-    def on_save_as_menu_item_activate(self, widget):
+    def on_save_as_config_menu_item_activate(self, widget):
         '''Save configuration as local file.'''
         if self.chooser is not None:
             self.chooser.destroy()
@@ -110,19 +121,19 @@ class PyGTKControl():
                        gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
-                self.path = self.chooser.get_filename()
-                self.on_save_menu_item_activate(widget)
+                self.config_path = self.chooser.get_filename()
+                self.on_save_config_menu_item_activate(widget)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
-    def on_save_menu_item_activate(self, widget):
+    def on_save_config_menu_item_activate(self, widget):
         '''Save configuration to file.'''
-        if self.path is None:
-            self.on_save_as_menu_item_activate(widget)
+        if self.config_path is None:
+            self.on_save_as_config_menu_item_activate(widget)
         else:
             config = self.get_current_config()
-            with open(self.path, 'w') as f:
+            with open(self.config_path, 'w') as f:
                 f.write(str(config))
 
     def get_current_config(self):
@@ -151,20 +162,9 @@ class PyGTKControl():
         table, tree = self.mime_tab[1]
         tree.get_model().append(None, ('', '', False, True, '', False, {}, ))
 
-    def on_new_menu_item_activate(self, widget = None):
-        '''Clears configuration.'''
-        if widget is not None:
-            self.path = None
-        table, tree = self.tabs['unknown']
-        tree.get_model().clear()
-        for tab in self.tabs:
-            table, tree = self.tabs[tab]
-            table.clear()
-            tree.make_clear()
-
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
-        self.on_new_menu_item_activate()
+        self.on_new_config_menu_item_activate()
         tabs = {}
         for tab in self.tabs:
             tabs[tab] = []
@@ -217,7 +217,7 @@ class PyGTKControl():
             self.widgets.get_widget('notebook').append_page(
                 panels, gtk.Label(tab))
 
-        self.on_new_menu_item_activate()
+        self.on_new_config_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 
     def construct_mime(self):



commit 36ebea61cf9228a29bffb1c444ab82421babcd35
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 25 15:49:55 2009 +0200

    New file menu.
    
    Extended, with buttons to open/save all three parts of config.

diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 0332e1d..44c8e5f 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -22,38 +22,174 @@
                     <property name="visible">True</property>
                     <child>
                       <widget class="GtkImageMenuItem" id="new_menu_item">
-                        <property name="label">gtk-new</property>
+                        <property name="label">New</property>
                         <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" 
handler="on_new_menu_item_activate"/>
+                        <property name="use_stock">False</property>
+                        <child>
+                          <widget class="GtkMenu" id="new_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="new_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">New 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_new_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="new_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">New 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_new_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="new_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">New 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_new_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="new_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-new</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkImageMenuItem" id="open_menu_item">
-                        <property name="label">gtk-open</property>
+                        <property name="label">Open</property>
                         <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" 
handler="on_open_menu_item_activate"/>
+                        <property name="use_stock">False</property>
+                        <child>
+                          <widget class="GtkMenu" id="open_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="open_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Open 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_open_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="open_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Open 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_open_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="open_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Open 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_open_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="open_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-open</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkImageMenuItem" id="save_menu_item">
-                        <property name="label">gtk-save</property>
+                        <property name="label">Save</property>
                         <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" 
handler="on_save_menu_item_activate"/>
+                        <property name="use_stock">False</property>
+                        <child>
+                          <widget class="GtkMenu" id="save_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="save_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-save</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkImageMenuItem" id="save_as_menu_item">
-                        <property name="label">gtk-save-as</property>
+                        <property name="label">Save as</property>
                         <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" 
handler="on_save_as_menu_item_activate"/>
+                        <property name="use_stock">False</property>
+                        <child>
+                          <widget class="GtkMenu" id="save_as_menu">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_as_config_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
server configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_as_config_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_as_mime_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
MIME configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_as_mime_menu_item_activate"/>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkMenuItem" 
id="save_as_vhost_menu_item">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Save 
VHost configuration</property>
+                                <property name="use_underline">True</property>
+                                <signal name="activate" 
handler="on_save_as_vhost_menu_item_activate"/>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="save_as_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-save-as</property>
+                            <property name="icon-size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                     </child>
                     <child>



commit 4aa358f6f612d9f9edb05422bb069e0580bd030a
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 24 22:38:21 2009 +0200

    Added definitions to MIME types.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 8f0b964..1bd8e66 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -217,7 +217,7 @@ class DefinitionTreeView(gtk.TreeView):
                 return True
         self.props.has_tooltip = True
         self.connect("query-tooltip", show_tooltip)
-        
+
     def make_def(self, current):
         '''Return row pointed by current exported as definition.'''
         model = self.get_model()
@@ -264,7 +264,7 @@ class DefinitionTreeView(gtk.TreeView):
         '''Sets up model reading from given config instance. If search in True
         will substitute alreaty present model data, if it is false will append
         all the definitions.'''
-        
+
         def get_value_and_attributes(definition):
             '''Get column values from definition instance.'''
             name = definition.get_name()
diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
index 832357a..d8980fa 100644
--- a/misc/PyGTK_Control/MIMEWidgets.py
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -25,7 +25,8 @@ class MimeTreeView(gtk.TreeView):
         gtk.TreeView.__init__(self, gtk.ListStore(
                 gobject.TYPE_STRING, # mime name
                 gobject.TYPE_PYOBJECT, # mime single attributes
-                gobject.TYPE_PYOBJECT)) # mime attribute lists
+                gobject.TYPE_PYOBJECT, # mime attribute lists
+                gobject.TYPE_PYOBJECT)) # mime definitions
         model = self.get_model()
         def mime_edited_handler(cell, path, text, data):
             model = data
@@ -45,12 +46,14 @@ class MimeTreeView(gtk.TreeView):
         self.scroll.add(self)
 
 class MimeTable(gtk.Table):
-    def __init__(self, tree):
+    def __init__(self, tree, def_tree, def_table):
         gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
                            3 * len(GUIConfig.mime_lists), 4)
 
         tree.connect('cursor-changed', self.cursor_changed)
         self.last_selected = None
+        self.def_tree = def_tree
+        self.def_table = def_table
 
         self.attributes = {}
         i = 0
@@ -102,13 +105,17 @@ class MimeTable(gtk.Table):
             i += 3
             
     def clear(self):
+        '''Clear input widgets (including connected definition widgets).'''
         for entry, check in self.attributes.itervalues():
             entry.set_text('')
             check.set_active(False)
         for model in self.mime_lists.itervalues():
             model.clear()
+        self.def_table.clear()
+        self.def_tree.get_model().clear()
 
     def save_changed(self, tree):
+        '''Save data from input widgets to model.'''
         if self.last_selected is not None:
             attributes = {}
             for attribute in self.attributes:
@@ -125,6 +132,7 @@ class MimeTable(gtk.Table):
             row = tree.get_model()[self.last_selected]
             row[1] = attributes
             row[2] = mime_lists
+            row[3] = self.def_table.make_def(self.def_tree)
 
     def cursor_changed(self, tree):
         self.save_changed(tree)
@@ -141,3 +149,4 @@ class MimeTable(gtk.Table):
             model = self.mime_lists[mime_list]
             for element in row[2][mime_list]:
                 model.append((element, ))
+        self.def_tree.set_up(row[3], False)
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 4301ecb..fca487a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -136,7 +136,7 @@ class PyGTKControl():
     def on_add_unknown_definition_menu_item_activate(self, widget):
         '''Adds a new definition to unknown tab.'''
         table, tree = self.tabs['unknown']
-        tree.get_model().append(None, ('', '', False,True, '', False, {}, ))
+        tree.get_model().append(None, ('', '', False, True, '', False, {}, ))
 
     def on_add_mime_type_menu_item_activate(self, widget):
         '''Adds a new MIME type.'''
@@ -144,7 +144,12 @@ class PyGTKControl():
         mime_lists = {}
         for mime_list in GUIConfig.mime_lists:
             mime_lists[mime_list] = []
-        tree.get_model().append(('', {}, mime_lists, ))
+        tree.get_model().append(('', {}, mime_lists, [], ))
+
+    def on_add_definition_to_mime_menu_item_activate(self, widget):
+        '''Adds a definition to currently selected MIME type.'''
+        table, tree = self.mime_tab[1]
+        tree.get_model().append(None, ('', '', False, True, '', False, {}, ))
 
     def on_new_menu_item_activate(self, widget = None):
         '''Clears configuration.'''
@@ -167,7 +172,7 @@ class PyGTKControl():
             name = definition.get_name()
             tab_name = self.options[name] if name in self.options else 
'unknown'
             tabs[tab_name].append(definition)
-            
+
         for tab in tabs:
             tree = self.tabs[tab][1]
             tree.set_up(tabs[tab], tab != 'unknown')
@@ -218,25 +223,24 @@ class PyGTKControl():
     def construct_mime(self):
         '''Reads mime options from file and prepares GUI.'''
         vpanels = gtk.VPaned()
-        
+
         panels = gtk.HPaned()
-        tree = MimeTreeView()
-        panels.pack1(tree.scroll, True, False)
-        table = MimeTable(tree)
-        panels.pack2(table, False, False)
-        self.mime_tab = (table, tree, )
+        def_tree = DefinitionTreeView()
+        panels.pack1(def_tree.scroll, True, False)
+        def_table = DefinitionTable(def_tree)
+        panels.pack2(def_table, False, False)
 
-        vpanels.pack1(panels)
+        vpanels.pack2(panels)
 
         panels = gtk.HPaned()
-        tree = DefinitionTreeView()
+        tree = MimeTreeView()
         panels.pack1(tree.scroll, True, False)
-        table = DefinitionTable(tree)
+        table = MimeTable(tree, def_tree, def_table)
         panels.pack2(table, False, False)
-        self.mime_tab = (self.mime_tab, (table, tree, ))
 
-        vpanels.pack2(panels)
+        vpanels.pack1(panels)
         
+        self.mime_tab = ((table, tree, ), (def_table, def_tree), )
         self.widgets.get_widget('notebook').append_page(
             vpanels, gtk.Label('MIME Type'))
 
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 6806292..0332e1d 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -185,6 +185,20 @@
                         </child>
                       </widget>
                     </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="add_definition_to_mime_menu_item">
+                        <property name="label">Add definition to MIME 
type</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_add_definition_to_mime_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" 
id="add_definition_to_mime_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-add</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
                   </widget>
                 </child>
               </widget>



commit a02ec8f3d2954ec1b4bac304447a07ff15c4497d
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 24 21:14:39 2009 +0200

    Refactored set_up.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 94f9004..8f0b964 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -242,3 +242,70 @@ class DefinitionTreeView(gtk.TreeView):
                     definitions.append(definition)
                 i = model.iter_next(i)
             return DefinitionTree(name, definitions, attributes)
+
+    def make_clear(self):
+        '''Remove all sub-definitions, reset values of level-1 definitions.'''
+        model = self.get_model()
+        self.get_selection().unselect_all()
+        i = model.iter_children(None)
+        while i is not None: # iterate over options
+            row = model[i]
+            row[3] = False
+            row[4] = ''
+            row[5] = False
+            row[6] = {}
+            child = model.iter_children(i)
+            if child is not None: # remove node children
+                while model.remove(child):
+                    pass
+            i = model.iter_next(i)
+
+    def set_up(self, definitions, search):
+        '''Sets up model reading from given config instance. If search in True
+        will substitute alreaty present model data, if it is false will append
+        all the definitions.'''
+        
+        def get_value_and_attributes(definition):
+            '''Get column values from definition instance.'''
+            name = definition.get_name()
+            enabled = True
+            try:
+                value = definition.get_attribute('value')
+                value_check = True
+            except KeyError:
+                value = ''
+                value_check = False
+            attributes = definition.get_attributes()
+            attributes.pop('value', None)
+            return (name, enabled, value, value_check, attributes, )
+
+        def add_children(parent, definition):
+            '''Insert sub-definitions of given definition as children of given
+            parent tree iterator.'''
+            if isinstance(definition, DefinitionTree):
+                for d in definition.get_definitions():
+                    name, enabled, value, value_check, attributes = \
+                        get_value_and_attributes(d)
+                    i = self.get_model().append(
+                        parent,
+                        (name, '', False, enabled, value, value_check,
+                         attributes, ))
+                    add_children(i, d)
+
+        model = self.get_model()
+        for definition in definitions:
+            name, enabled, value, value_check, attributes = \
+                get_value_and_attributes(definition)
+            if search:
+                i = model.iter_children(None) # find this option
+                while model[i][0] != name:
+                    i = model.iter_next(i)
+            else:
+                i = model.append(None,
+                                 (name, '', False, False, '', False, {}, ))
+            row = model[i]
+            row[3] = enabled
+            row[4] = value
+            row[5] = value_check
+            row[6] = attributes
+            add_children(i, definition)
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 95d6b44..4301ecb 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -154,74 +154,23 @@ class PyGTKControl():
         tree.get_model().clear()
         for tab in self.tabs:
             table, tree = self.tabs[tab]
-            model = tree.get_model()
             table.clear()
-            tree.get_selection().unselect_all()
-            i = model.iter_children(None)
-            while i is not None: # iterate over options
-                row = model[i]
-                row[3] = False
-                row[4] = ''
-                row[5] = False
-                row[6] = {}
-                child = model.iter_children(i)
-                if child is not None: # remove node children
-                    while model.remove(child):
-                        pass
-                i = model.iter_next(i)
+            tree.make_clear()
 
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
-
-        def get_value_and_attributes(definition):
-            name = definition.get_name()
-            enabled = True
-            try:
-                value = definition.get_attribute('value')
-                value_check = True
-            except KeyError:
-                value = ''
-                value_check = False
-            attributes = definition.get_attributes()
-            attributes.pop('value', None)
-            return (name, enabled, value, value_check, attributes, )
-
-        def add_children(parent, definition, model):
-            if isinstance(definition, DefinitionTree):
-                for d in definition.get_definitions():
-                    name, enabled, value, value_check, attributes = \
-                        get_value_and_attributes(d)
-                    i = model.append(
-                        parent,
-                        (name, '', False, enabled, value, value_check,
-                         attributes, ))
-                    add_children(i, d, model)
-
         self.on_new_menu_item_activate()
+        tabs = {}
+        for tab in self.tabs:
+            tabs[tab] = []
         for definition in config.get_definitions():
-            name, enabled, value, value_check, attributes = \
-                get_value_and_attributes(definition)
-
-            if name not in self.options:
-                tab_name = 'unknown'
-            else:
-                tab_name = self.options[name]
-
-            tree = self.tabs[tab_name][1]
-            model = tree.get_model()
-            if tab_name != 'unknown':
-                i = model.iter_children(None) # find this option
-                while model[i][0] != name:
-                    i = model.iter_next(i)
-            else:
-                i = model.append(None,
-                                 (name, '', False, False, '', False, {}, ))
-            row = model[i]
-            row[3] = enabled
-            row[4] = value
-            row[5] = value_check
-            row[6] = attributes
-            add_children(i, definition, model)
+            name = definition.get_name()
+            tab_name = self.options[name] if name in self.options else 
'unknown'
+            tabs[tab_name].append(definition)
+            
+        for tab in tabs:
+            tree = self.tabs[tab][1]
+            tree.set_up(tabs[tab], tab != 'unknown')
 
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''



commit 4171c905b2321945b3ecf71ae65542a7db3c6a2c
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 24 20:33:38 2009 +0200

    Refactoring of configuration saving.

diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
index 85a9810..94f9004 100644
--- a/misc/PyGTK_Control/DefinitionWidgets.py
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -18,6 +18,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 
 import gtk
 import gobject
+from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 
 class DefinitionTable(gtk.Table):
     def __init__(self, tree):
@@ -117,6 +118,7 @@ class DefinitionTable(gtk.Table):
         self.attach(attributes_scroll, 0, 3, 7, 8)
 
     def clear(self):
+        '''Clear input widgets.'''
         self.enabled_field.set_active(False)
         self.value_field.set_text('')
         self.value_check_field.set_active(False)
@@ -124,6 +126,7 @@ class DefinitionTable(gtk.Table):
         self.last_selected = None
 
     def save_changed(self, tree):
+        '''Save data from input widgets to tree.'''
         if self.last_selected is not None:
             attributes = {}
             i = self.attributes_field.iter_children(None)
@@ -138,6 +141,7 @@ class DefinitionTable(gtk.Table):
             row[6] = attributes
 
     def cursor_changed(self, tree):
+        '''Save data, and display data of current selected row.'''
         self.save_changed(tree)
 
         self.clear()
@@ -151,9 +155,23 @@ class DefinitionTable(gtk.Table):
             self.attributes_field.append((attribute, row[5][attribute], ))
 
     def get_selected(self, tree):
+        '''Get iterator of currently selected row.'''
         model, selected = tree.get_selection().get_selected()
         return selected
 
+    def make_def(self, tree):
+        '''Export all data as list of definitions.'''
+        self.save_changed(tree)
+        model = tree.get_model()
+        definitions = []
+        i = model.iter_children(None)
+        while i is not None: # iterate over options
+            definition = tree.make_def(i)
+            if definition is not None:
+                definitions.append(definition)
+            i = model.iter_next(i)
+        return definitions
+
 class DefinitionTreeView(gtk.TreeView):
     def __init__(self):
         gtk.TreeView.__init__(self, gtk.TreeStore(
@@ -199,3 +217,28 @@ class DefinitionTreeView(gtk.TreeView):
                 return True
         self.props.has_tooltip = True
         self.connect("query-tooltip", show_tooltip)
+        
+    def make_def(self, current):
+        '''Return row pointed by current exported as definition.'''
+        model = self.get_model()
+        row = model[current]
+        name = row[0]
+        enabled = row[3]
+        value = row[4]
+        value_check = row[5]
+        attributes = row[6]
+        if value_check:
+            attributes['value'] = value
+        if not enabled:
+            return None
+        i = model.iter_children(current)
+        if i is None:
+            return DefinitionElement(name, attributes)
+        else:
+            definitions = []
+            while i is not None: # iterate over children
+                definition = self.make_def(i)
+                if definition is not None:
+                    definitions.append(definition)
+                i = model.iter_next(i)
+            return DefinitionTree(name, definitions, attributes)
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 79f7991..95d6b44 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -23,7 +23,6 @@ import gobject
 import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
 from MyServer.pycontrollib.controller import Controller
-from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 from AboutWindow import About
 from ConnectionWindow import Connection
 from DefinitionWidgets import DefinitionTable, DefinitionTreeView
@@ -128,41 +127,10 @@ class PyGTKControl():
 
     def get_current_config(self):
         '''Returns current configuration as MyServerConfig instance.'''
-
-        def make_def(current, model):
-            row = model[current]
-            name = row[0]
-            enabled = row[3]
-            value = row[4]
-            value_check = row[5]
-            attributes = row[6]
-            if value_check:
-                attributes['value'] = value
-            if not enabled:
-                return None
-            i = model.iter_children(current)
-            if i is None:
-                return DefinitionElement(name, attributes)
-            else:
-                definitions = []
-                while i is not None: # iterate over children
-                    definition = make_def(i, model)
-                    if definition is not None:
-                        definitions.append(definition)
-                    i = model.iter_next(i)
-                return DefinitionTree(name, definitions, attributes)
-
         definitions = []
         for tab in self.tabs:
             table, tree = self.tabs[tab]
-            model = tree.get_model()
-            table.save_changed(tree)
-            i = model.iter_children(None)
-            while i is not None: # iterate over options
-                definition = make_def(i, model)
-                if definition is not None:
-                    definitions.append(definition)
-                i = model.iter_next(i)
+            definitions += table.make_def(tree)
         return MyServerConfig(definitions)
 
     def on_add_unknown_definition_menu_item_activate(self, widget):
@@ -172,7 +140,7 @@ class PyGTKControl():
 
     def on_add_mime_type_menu_item_activate(self, widget):
         '''Adds a new MIME type.'''
-        table, tree = self.tabs['MIME'][0]
+        table, tree = self.mime_tab[0]
         mime_lists = {}
         for mime_list in GUIConfig.mime_lists:
             mime_lists[mime_list] = []
@@ -307,7 +275,7 @@ class PyGTKControl():
         panels.pack1(tree.scroll, True, False)
         table = MimeTable(tree)
         panels.pack2(table, False, False)
-        self.tabs['MIME'] = (table, tree, )
+        self.mime_tab = (table, tree, )
 
         vpanels.pack1(panels)
 
@@ -316,7 +284,7 @@ class PyGTKControl():
         panels.pack1(tree.scroll, True, False)
         table = DefinitionTable(tree)
         panels.pack2(table, False, False)
-        self.tabs['MIME'] = (self.tabs['MIME'], (table, tree, ))
+        self.mime_tab = (self.mime_tab, (table, tree, ))
 
         vpanels.pack2(panels)
         



commit 716ad749f0f326562db1a80e08e3cc8b6faf2e21
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 24 17:58:54 2009 +0200

    Split MyServerControl to several files.

diff --git a/misc/PyGTK_Control/AboutWindow.py 
b/misc/PyGTK_Control/AboutWindow.py
new file mode 100644
index 0000000..252081f
--- /dev/null
+++ b/misc/PyGTK_Control/AboutWindow.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gtk.glade
+
+class About():
+    '''GNU MyServer Control about window.'''
+
+    def __init__(self):
+        self.gladefile = 'PyGTKControl.glade'
+        self.widgets = gtk.glade.XML(self.gladefile, 'aboutdialog')
+        self.widgets.signal_autoconnect(self)
+
+    def on_aboutdialog_response(self, widget, response):
+        widget.destroy()
diff --git a/misc/PyGTK_Control/ConnectionWindow.py 
b/misc/PyGTK_Control/ConnectionWindow.py
new file mode 100644
index 0000000..16d4ddf
--- /dev/null
+++ b/misc/PyGTK_Control/ConnectionWindow.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gtk.glade
+
+class Connection():
+    def __init__(self):
+        self.gladefile = 'PyGTKControl.glade'
+        self.widgets = gtk.glade.XML(self.gladefile, 'connectiondialog')
+        self.widgets.signal_autoconnect(self)
+
+    def destroy(self):
+        '''Destroys this widget.'''
+        self.widgets.get_widget('connectiondialog').destroy()
+
+    def on_cancel_button_clicked(self, widget):
+        self.destroy()
+
+    def get_host(self):
+        return self.widgets.get_widget('host_entry').get_text()
+
+    def get_port(self):
+        return self.widgets.get_widget('port_entry').get_text()
+
+    def get_username(self):
+        return self.widgets.get_widget('username_entry').get_text()
+
+    def get_password(self):
+        return self.widgets.get_widget('password_entry').get_text()
diff --git a/misc/PyGTK_Control/DefinitionWidgets.py 
b/misc/PyGTK_Control/DefinitionWidgets.py
new file mode 100644
index 0000000..85a9810
--- /dev/null
+++ b/misc/PyGTK_Control/DefinitionWidgets.py
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gobject
+
+class DefinitionTable(gtk.Table):
+    def __init__(self, tree):
+        gtk.Table.__init__(self, 8, 3)
+
+        tree.connect('cursor-changed', self.cursor_changed)
+        self.last_selected = None
+
+        enabled_label = gtk.Label('enabled:')
+        self.enabled_field = enabled_checkbutton = gtk.CheckButton()
+        enabled_checkbutton.set_tooltip_text('If not active, definition won\'t 
be included in saved configuration.')
+        self.attach(enabled_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(enabled_checkbutton, 1, 3, 0, 1, yoptions = gtk.FILL)
+
+        value_label = gtk.Label('value:')
+        self.value_field = value_entry = gtk.Entry()
+        self.value_check_field = value_checkbutton = gtk.CheckButton()
+        value_checkbutton.set_tooltip_text('If active value will be used.')
+        value_checkbutton.set_active(True)
+        def toggle_value(button, tree):
+            # don't allow value for definition with children
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None and model.iter_children(selected) is not 
None:
+                button.set_active(False)
+                return
+            active = button.get_active()
+            self.value_field.set_editable(active)
+        value_checkbutton.connect('toggled', toggle_value, tree)
+        self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
+        self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+
+        add_definition_button = gtk.Button('add sub-definition')
+        def add_sub_definition(button):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None:
+                model.append(selected, ('', '', False, True, '', False, {}, ))
+                self.value_check_field.set_active(False) # auto disable value
+                self.value_field.set_editable(False)
+        add_definition_button.connect('clicked', add_sub_definition)
+        self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
+
+        remove_definition_button = gtk.Button('remove this definition')
+        def remove_definition(button):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None and not model[selected][2]:
+                model.remove(selected)
+                self.clear()
+        remove_definition_button.connect('clicked', remove_definition)
+        self.attach(remove_definition_button, 0, 3, 3, 4, yoptions = gtk.FILL)
+
+        add_attribute_button = gtk.Button('add attribute')
+        remove_attribute_button = gtk.Button('remove attribute')
+        add_attribute_button.connect(
+            'clicked',
+            lambda button: attributes_model.append(('', '', )))
+        def remove_attribute(button):
+            model, selected = attributes_list.get_selection().get_selected()
+            if selected is not None:
+                model.remove(selected)
+        remove_attribute_button.connect('clicked', remove_attribute)
+        self.attach(add_attribute_button, 0, 3, 4, 5, yoptions = gtk.FILL)
+        self.attach(remove_attribute_button, 0, 3, 5, 6, yoptions = gtk.FILL)
+
+        attributes_label = gtk.Label('attributes:')
+        attributes_list = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING,
+                                                     gobject.TYPE_STRING))
+        attributes_scroll = gtk.ScrolledWindow()
+        attributes_scroll.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
+        attributes_scroll.set_shadow_type(gtk.SHADOW_OUT)
+        attributes_scroll.set_border_width(5)
+        attributes_scroll.add(attributes_list)
+        self.attributes_field = attributes_model = attributes_list.get_model()
+
+        def edited_handler(cell, path, text, data):
+            model, col = data
+            model[path][col] = text
+        variable_column = gtk.TreeViewColumn('variable')
+        variable_renderer = gtk.CellRendererText()
+        variable_renderer.set_property('editable', True)
+        variable_renderer.connect('edited', edited_handler,
+                                  (attributes_model, 0, ))
+        variable_column.pack_start(variable_renderer)
+        variable_column.add_attribute(variable_renderer, 'text', 0)
+
+        value_column = gtk.TreeViewColumn('value')
+        value_renderer = gtk.CellRendererText()
+        value_renderer.set_property('editable', True)
+        value_renderer.connect('edited', edited_handler,
+                                  (attributes_model, 1, ))
+        value_column.pack_start(value_renderer)
+        value_column.add_attribute(value_renderer, 'text', 1)
+
+        attributes_list.append_column(variable_column)
+        attributes_list.append_column(value_column)
+        self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
+        self.attach(attributes_scroll, 0, 3, 7, 8)
+
+    def clear(self):
+        self.enabled_field.set_active(False)
+        self.value_field.set_text('')
+        self.value_check_field.set_active(False)
+        self.attributes_field.clear()
+        self.last_selected = None
+
+    def save_changed(self, tree):
+        if self.last_selected is not None:
+            attributes = {}
+            i = self.attributes_field.iter_children(None)
+            while i is not None: # iterate over attributes
+                attributes[self.attributes_field.get_value(i, 0)] = \
+                    self.attributes_field.get_value(i, 1)
+                i = self.attributes_field.iter_next(i)
+            row = tree.get_model()[self.last_selected]
+            row[3] = self.enabled_field.get_active()
+            row[4] = self.value_field.get_text()
+            row[5] = self.value_check_field.get_active()
+            row[6] = attributes
+
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+
+        self.clear()
+
+        self.last_selected = current = self.get_selected(tree)
+        row = tree.get_model()[current]
+        self.enabled_field.set_active(row[3])
+        self.value_field.set_text(row[4])
+        self.value_check_field.set_active(row[5])
+        for attribute in row[6]:
+            self.attributes_field.append((attribute, row[5][attribute], ))
+
+    def get_selected(self, tree):
+        model, selected = tree.get_selection().get_selected()
+        return selected
+
+class DefinitionTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.TreeStore(
+                gobject.TYPE_STRING, # option name
+                gobject.TYPE_STRING, # option tooltip
+                gobject.TYPE_BOOLEAN, # True if option is known
+                gobject.TYPE_BOOLEAN, # enabled
+                gobject.TYPE_STRING, # value
+                gobject.TYPE_BOOLEAN, # value_check
+                gobject.TYPE_PYOBJECT)) # attributes dict
+        model = self.get_model()
+        def name_edited_handler(cell, path, text, data):
+            model = data
+            row = model[path]
+            if not row[2]: # don't edit names of known options
+                row[0] = text
+        name_renderer = gtk.CellRendererText()
+        name_renderer.set_property('editable', True)
+        name_renderer.connect('edited', name_edited_handler, model)
+        name_column = gtk.TreeViewColumn('name')
+        name_column.pack_start(name_renderer)
+        name_column.add_attribute(name_renderer, 'text', 0)
+        self.append_column(name_column)
+        value_renderer = gtk.CellRendererText()
+        value_column = gtk.TreeViewColumn('value')
+        value_column.pack_start(value_renderer)
+        value_column.add_attribute(value_renderer, 'text', 4)
+        self.append_column(value_column)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+        def show_tooltip(widget, x, y, keyboard_tip, tooltip):
+            if not widget.get_tooltip_context(x, y, keyboard_tip):
+                return False
+            else:
+                model, path, it = widget.get_tooltip_context(x, y, 
keyboard_tip)
+                tooltip.set_text(model[it][1])
+                widget.set_tooltip_row(tooltip, path)
+                return True
+        self.props.has_tooltip = True
+        self.connect("query-tooltip", show_tooltip)
diff --git a/misc/PyGTK_Control/MIMEWidgets.py 
b/misc/PyGTK_Control/MIMEWidgets.py
new file mode 100644
index 0000000..832357a
--- /dev/null
+++ b/misc/PyGTK_Control/MIMEWidgets.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gobject
+import GUIConfig
+
+class MimeTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.ListStore(
+                gobject.TYPE_STRING, # mime name
+                gobject.TYPE_PYOBJECT, # mime single attributes
+                gobject.TYPE_PYOBJECT)) # mime attribute lists
+        model = self.get_model()
+        def mime_edited_handler(cell, path, text, data):
+            model = data
+            model[path][0] = text
+        mime_renderer = gtk.CellRendererText()
+        mime_renderer.set_property('editable', True)
+        mime_renderer.connect('edited', mime_edited_handler, model)
+        mime_column = gtk.TreeViewColumn('MIME Type')
+        mime_column.pack_start(mime_renderer)
+        mime_column.add_attribute(mime_renderer, 'text', 0)
+        self.append_column(mime_column)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+class MimeTable(gtk.Table):
+    def __init__(self, tree):
+        gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
+                           3 * len(GUIConfig.mime_lists), 4)
+
+        tree.connect('cursor-changed', self.cursor_changed)
+        self.last_selected = None
+
+        self.attributes = {}
+        i = 0
+        for attribute in GUIConfig.mime_attributes:
+            label = gtk.Label(attribute)
+            entry = gtk.Entry()
+            check = gtk.CheckButton()
+            self.attributes[attribute] = (entry, check, )
+            self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            i += 1
+        self.mime_lists = {}
+        for mime_list in GUIConfig.mime_lists:
+            tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
+            tree_model = tree.get_model()
+            def tree_edited_handler(cell, path, text, data):
+                model = data
+                model[path][0] = text
+            tree_renderer = gtk.CellRendererText()
+            tree_renderer.set_property('editable', True)
+            tree_renderer.connect('edited', tree_edited_handler, tree_model)
+            tree_column = gtk.TreeViewColumn(mime_list)
+            tree_column.pack_start(tree_renderer)
+            tree_column.add_attribute(tree_renderer, 'text', 0)
+            tree.append_column(tree_column)
+            tree_scroll = gtk.ScrolledWindow()
+            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+            tree_scroll.set_border_width(5)
+            tree_scroll.add(tree)
+
+            def add_to_tree(button, model):
+                model.append(('', ))
+            add_button = gtk.Button('Add')
+            add_button.connect('clicked', add_to_tree, tree_model)
+            def remove_from_tree(button, tree):
+                model, selected = tree.get_selection().get_selected()
+                if selected is not None:
+                    model.remove(selected)
+            remove_button = gtk.Button('Remove')
+            remove_button.connect('clicked', remove_from_tree, tree)
+
+            self.mime_lists[mime_list] = tree_model
+            self.attach(tree_scroll, 0, 2, i, i + 3)
+            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
+            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            i += 3
+            
+    def clear(self):
+        for entry, check in self.attributes.itervalues():
+            entry.set_text('')
+            check.set_active(False)
+        for model in self.mime_lists.itervalues():
+            model.clear()
+
+    def save_changed(self, tree):
+        if self.last_selected is not None:
+            attributes = {}
+            for attribute in self.attributes:
+                entry, check = self.attributes[attribute]
+                attributes[attribute] = (entry.get_text(), check.get_active(), 
)
+            mime_lists = {}
+            for mime_list in GUIConfig.mime_lists:
+                container = mime_lists[mime_list] = []
+                model = self.mime_lists[mime_list]
+                i = model.iter_children(None)
+                while i is not None: # iterate over list elements
+                    container.append(model.get_value(i, 0))
+                    i = model.iter_next(i)
+            row = tree.get_model()[self.last_selected]
+            row[1] = attributes
+            row[2] = mime_lists
+
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+
+        self.clear()
+
+        self.last_selected = current = tree.get_selection().get_selected()[1]
+        row = tree.get_model()[current]
+        for attribute in row[1]:
+            entry, check = self.attributes[attribute]
+            entry.set_text(row[1][attribute][0])
+            check.set_active(row[1][attribute][1])
+        for mime_list in row[2]:
+            model = self.mime_lists[mime_list]
+            for element in row[2][mime_list]:
+                model.append((element, ))
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index c313af1..79f7991 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -18,352 +18,16 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
 import gtk
-import gobject
 import gtk.glade
+import gobject
 import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
 from MyServer.pycontrollib.controller import Controller
 from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
-
-class About():
-    '''GNU MyServer Control about window.'''
-
-    def __init__(self):
-        self.gladefile = 'PyGTKControl.glade'
-        self.widgets = gtk.glade.XML(self.gladefile, 'aboutdialog')
-        self.widgets.signal_autoconnect(self)
-
-    def on_aboutdialog_response(self, widget, response):
-        widget.destroy()
-
-class Connection():
-    def __init__(self):
-        self.gladefile = 'PyGTKControl.glade'
-        self.widgets = gtk.glade.XML(self.gladefile, 'connectiondialog')
-        self.widgets.signal_autoconnect(self)
-
-    def destroy(self):
-        '''Destroys this widget.'''
-        self.widgets.get_widget('connectiondialog').destroy()
-
-    def on_cancel_button_clicked(self, widget):
-        self.destroy()
-
-    def get_host(self):
-        return self.widgets.get_widget('host_entry').get_text()
-
-    def get_port(self):
-        return self.widgets.get_widget('port_entry').get_text()
-
-    def get_username(self):
-        return self.widgets.get_widget('username_entry').get_text()
-
-    def get_password(self):
-        return self.widgets.get_widget('password_entry').get_text()
-
-class EditionTable(gtk.Table):
-    def __init__(self, tree):
-        gtk.Table.__init__(self, 8, 3)
-
-        tree.connect('cursor-changed', self.cursor_changed)
-        self.last_selected = None
-
-        enabled_label = gtk.Label('enabled:')
-        self.enabled_field = enabled_checkbutton = gtk.CheckButton()
-        enabled_checkbutton.set_tooltip_text('If not active, definition won\'t 
be included in saved configuration.')
-        self.attach(enabled_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
-        self.attach(enabled_checkbutton, 1, 3, 0, 1, yoptions = gtk.FILL)
-
-        value_label = gtk.Label('value:')
-        self.value_field = value_entry = gtk.Entry()
-        self.value_check_field = value_checkbutton = gtk.CheckButton()
-        value_checkbutton.set_tooltip_text('If active value will be used.')
-        value_checkbutton.set_active(True)
-        def toggle_value(button, tree):
-            # don't allow value for definition with children
-            model, selected = tree.get_selection().get_selected()
-            if selected is not None and model.iter_children(selected) is not 
None:
-                button.set_active(False)
-                return
-            active = button.get_active()
-            self.value_field.set_editable(active)
-        value_checkbutton.connect('toggled', toggle_value, tree)
-        self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
-        self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
-        self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
-
-        add_definition_button = gtk.Button('add sub-definition')
-        def add_sub_definition(button):
-            model, selected = tree.get_selection().get_selected()
-            if selected is not None:
-                model.append(selected, ('', '', False, True, '', False, {}, ))
-                self.value_check_field.set_active(False) # auto disable value
-                self.value_field.set_editable(False)
-        add_definition_button.connect('clicked', add_sub_definition)
-        self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
-
-        remove_definition_button = gtk.Button('remove this definition')
-        def remove_definition(button):
-            model, selected = tree.get_selection().get_selected()
-            if selected is not None and not model[selected][2]:
-                model.remove(selected)
-                self.clear()
-        remove_definition_button.connect('clicked', remove_definition)
-        self.attach(remove_definition_button, 0, 3, 3, 4, yoptions = gtk.FILL)
-
-        add_attribute_button = gtk.Button('add attribute')
-        remove_attribute_button = gtk.Button('remove attribute')
-        add_attribute_button.connect(
-            'clicked',
-            lambda button: attributes_model.append(('', '', )))
-        def remove_attribute(button):
-            model, selected = attributes_list.get_selection().get_selected()
-            if selected is not None:
-                model.remove(selected)
-        remove_attribute_button.connect('clicked', remove_attribute)
-        self.attach(add_attribute_button, 0, 3, 4, 5, yoptions = gtk.FILL)
-        self.attach(remove_attribute_button, 0, 3, 5, 6, yoptions = gtk.FILL)
-
-        attributes_label = gtk.Label('attributes:')
-        attributes_list = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING,
-                                                     gobject.TYPE_STRING))
-        attributes_scroll = gtk.ScrolledWindow()
-        attributes_scroll.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
-        attributes_scroll.set_shadow_type(gtk.SHADOW_OUT)
-        attributes_scroll.set_border_width(5)
-        attributes_scroll.add(attributes_list)
-        self.attributes_field = attributes_model = attributes_list.get_model()
-
-        def edited_handler(cell, path, text, data):
-            model, col = data
-            model[path][col] = text
-        variable_column = gtk.TreeViewColumn('variable')
-        variable_renderer = gtk.CellRendererText()
-        variable_renderer.set_property('editable', True)
-        variable_renderer.connect('edited', edited_handler,
-                                  (attributes_model, 0, ))
-        variable_column.pack_start(variable_renderer)
-        variable_column.add_attribute(variable_renderer, 'text', 0)
-
-        value_column = gtk.TreeViewColumn('value')
-        value_renderer = gtk.CellRendererText()
-        value_renderer.set_property('editable', True)
-        value_renderer.connect('edited', edited_handler,
-                                  (attributes_model, 1, ))
-        value_column.pack_start(value_renderer)
-        value_column.add_attribute(value_renderer, 'text', 1)
-
-        attributes_list.append_column(variable_column)
-        attributes_list.append_column(value_column)
-        self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
-        self.attach(attributes_scroll, 0, 3, 7, 8)
-
-    def clear(self):
-        self.enabled_field.set_active(False)
-        self.value_field.set_text('')
-        self.value_check_field.set_active(False)
-        self.attributes_field.clear()
-        self.last_selected = None
-
-    def save_changed(self, tree):
-        if self.last_selected is not None:
-            attributes = {}
-            i = self.attributes_field.iter_children(None)
-            while i is not None: # iterate over attributes
-                attributes[self.attributes_field.get_value(i, 0)] = \
-                    self.attributes_field.get_value(i, 1)
-                i = self.attributes_field.iter_next(i)
-            row = tree.get_model()[self.last_selected]
-            row[3] = self.enabled_field.get_active()
-            row[4] = self.value_field.get_text()
-            row[5] = self.value_check_field.get_active()
-            row[6] = attributes
-
-    def cursor_changed(self, tree):
-        self.save_changed(tree)
-
-        self.clear()
-
-        self.last_selected = current = self.get_selected(tree)
-        row = tree.get_model()[current]
-        self.enabled_field.set_active(row[3])
-        self.value_field.set_text(row[4])
-        self.value_check_field.set_active(row[5])
-        for attribute in row[6]:
-            self.attributes_field.append((attribute, row[5][attribute], ))
-
-    def get_selected(self, tree):
-        model, selected = tree.get_selection().get_selected()
-        return selected
-
-class DefinitionTreeView(gtk.TreeView):
-    def __init__(self):
-        gtk.TreeView.__init__(self, gtk.TreeStore(
-                gobject.TYPE_STRING, # option name
-                gobject.TYPE_STRING, # option tooltip
-                gobject.TYPE_BOOLEAN, # True if option is known
-                gobject.TYPE_BOOLEAN, # enabled
-                gobject.TYPE_STRING, # value
-                gobject.TYPE_BOOLEAN, # value_check
-                gobject.TYPE_PYOBJECT)) # attributes dict
-        model = self.get_model()
-        def name_edited_handler(cell, path, text, data):
-            model = data
-            row = model[path]
-            if not row[2]: # don't edit names of known options
-                row[0] = text
-        name_renderer = gtk.CellRendererText()
-        name_renderer.set_property('editable', True)
-        name_renderer.connect('edited', name_edited_handler, model)
-        name_column = gtk.TreeViewColumn('name')
-        name_column.pack_start(name_renderer)
-        name_column.add_attribute(name_renderer, 'text', 0)
-        self.append_column(name_column)
-        value_renderer = gtk.CellRendererText()
-        value_column = gtk.TreeViewColumn('value')
-        value_column.pack_start(value_renderer)
-        value_column.add_attribute(value_renderer, 'text', 4)
-        self.append_column(value_column)
-
-        self.scroll = gtk.ScrolledWindow()
-        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
-        self.scroll.set_border_width(5)
-        self.scroll.add(self)
-
-        def show_tooltip(widget, x, y, keyboard_tip, tooltip):
-            if not widget.get_tooltip_context(x, y, keyboard_tip):
-                return False
-            else:
-                model, path, it = widget.get_tooltip_context(x, y, 
keyboard_tip)
-                tooltip.set_text(model[it][1])
-                widget.set_tooltip_row(tooltip, path)
-                return True
-        self.props.has_tooltip = True
-        self.connect("query-tooltip", show_tooltip)
-
-class MimeTreeView(gtk.TreeView):
-    def __init__(self):
-        gtk.TreeView.__init__(self, gtk.ListStore(
-                gobject.TYPE_STRING, # mime name
-                gobject.TYPE_PYOBJECT, # mime single attributes
-                gobject.TYPE_PYOBJECT)) # mime attribute lists
-        model = self.get_model()
-        def mime_edited_handler(cell, path, text, data):
-            model = data
-            model[path][0] = text
-        mime_renderer = gtk.CellRendererText()
-        mime_renderer.set_property('editable', True)
-        mime_renderer.connect('edited', mime_edited_handler, model)
-        mime_column = gtk.TreeViewColumn('MIME Type')
-        mime_column.pack_start(mime_renderer)
-        mime_column.add_attribute(mime_renderer, 'text', 0)
-        self.append_column(mime_column)
-
-        self.scroll = gtk.ScrolledWindow()
-        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
-        self.scroll.set_border_width(5)
-        self.scroll.add(self)
-
-class MimeTable(gtk.Table):
-    def __init__(self, tree):
-        gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
-                           3 * len(GUIConfig.mime_lists), 4)
-
-        tree.connect('cursor-changed', self.cursor_changed)
-        self.last_selected = None
-
-        self.attributes = {}
-        i = 0
-        for attribute in GUIConfig.mime_attributes:
-            label = gtk.Label(attribute)
-            entry = gtk.Entry()
-            check = gtk.CheckButton()
-            self.attributes[attribute] = (entry, check, )
-            self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
-            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
-            i += 1
-        self.mime_lists = {}
-        for mime_list in GUIConfig.mime_lists:
-            tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
-            tree_model = tree.get_model()
-            def tree_edited_handler(cell, path, text, data):
-                model = data
-                model[path][0] = text
-            tree_renderer = gtk.CellRendererText()
-            tree_renderer.set_property('editable', True)
-            tree_renderer.connect('edited', tree_edited_handler, tree_model)
-            tree_column = gtk.TreeViewColumn(mime_list)
-            tree_column.pack_start(tree_renderer)
-            tree_column.add_attribute(tree_renderer, 'text', 0)
-            tree.append_column(tree_column)
-            tree_scroll = gtk.ScrolledWindow()
-            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
-            tree_scroll.set_border_width(5)
-            tree_scroll.add(tree)
-
-            def add_to_tree(button, model):
-                model.append(('', ))
-            add_button = gtk.Button('Add')
-            add_button.connect('clicked', add_to_tree, tree_model)
-            def remove_from_tree(button, tree):
-                model, selected = tree.get_selection().get_selected()
-                if selected is not None:
-                    model.remove(selected)
-            remove_button = gtk.Button('Remove')
-            remove_button.connect('clicked', remove_from_tree, tree)
-
-            self.mime_lists[mime_list] = tree_model
-            self.attach(tree_scroll, 0, 2, i, i + 3)
-            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
-            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
-            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
-            i += 3
-            
-    def clear(self):
-        for entry, check in self.attributes.itervalues():
-            entry.set_text('')
-            check.set_active(False)
-        for model in self.mime_lists.itervalues():
-            model.clear()
-
-    def save_changed(self, tree):
-        if self.last_selected is not None:
-            attributes = {}
-            for attribute in self.attributes:
-                entry, check = self.attributes[attribute]
-                attributes[attribute] = (entry.get_text(), check.get_active(), 
)
-            mime_lists = {}
-            for mime_list in GUIConfig.mime_lists:
-                container = mime_lists[mime_list] = []
-                model = self.mime_lists[mime_list]
-                i = model.iter_children(None)
-                while i is not None: # iterate over list elements
-                    container.append(model.get_value(i, 0))
-                    i = model.iter_next(i)
-            row = tree.get_model()[self.last_selected]
-            row[1] = attributes
-            row[2] = mime_lists
-
-    def cursor_changed(self, tree):
-        self.save_changed(tree)
-
-        self.clear()
-
-        self.last_selected = current = tree.get_selection().get_selected()[1]
-        row = tree.get_model()[current]
-        for attribute in row[1]:
-            entry, check = self.attributes[attribute]
-            entry.set_text(row[1][attribute][0])
-            check.set_active(row[1][attribute][1])
-        for mime_list in row[2]:
-            model = self.mime_lists[mime_list]
-            for element in row[2][mime_list]:
-                model.append((element, ))
-
+from AboutWindow import About
+from ConnectionWindow import Connection
+from DefinitionWidgets import DefinitionTable, DefinitionTreeView
+from MIMEWidgets import MimeTable, MimeTreeView
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -616,7 +280,7 @@ class PyGTKControl():
             tree = DefinitionTreeView()
             tree_model = tree.get_model()
             panels.pack1(tree.scroll, True, False)
-            table = EditionTable(tree)
+            table = DefinitionTable(tree)
             panels.pack2(table, False, False)
 
             self.tabs[tab] = (table, tree, )
@@ -650,7 +314,7 @@ class PyGTKControl():
         panels = gtk.HPaned()
         tree = DefinitionTreeView()
         panels.pack1(tree.scroll, True, False)
-        table = EditionTable(tree)
+        table = DefinitionTable(tree)
         panels.pack2(table, False, False)
         self.tabs['MIME'] = (self.tabs['MIME'], (table, tree, ))
 



commit f59a03ce25be48ee894e1db2947bdfd03ac93d71
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 24 00:06:15 2009 +0200

    Editable MIME types.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index fb401fc..c313af1 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -244,7 +244,7 @@ class DefinitionTreeView(gtk.TreeView):
 
 class MimeTreeView(gtk.TreeView):
     def __init__(self):
-        gtk.TreeView.__init__(self, gtk.TreeStore(
+        gtk.TreeView.__init__(self, gtk.ListStore(
                 gobject.TYPE_STRING, # mime name
                 gobject.TYPE_PYOBJECT, # mime single attributes
                 gobject.TYPE_PYOBJECT)) # mime attribute lists
@@ -267,18 +267,25 @@ class MimeTreeView(gtk.TreeView):
         self.scroll.add(self)
 
 class MimeTable(gtk.Table):
-    def __init__(self):
+    def __init__(self, tree):
         gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
                            3 * len(GUIConfig.mime_lists), 4)
+
+        tree.connect('cursor-changed', self.cursor_changed)
+        self.last_selected = None
+
+        self.attributes = {}
         i = 0
         for attribute in GUIConfig.mime_attributes:
             label = gtk.Label(attribute)
             entry = gtk.Entry()
             check = gtk.CheckButton()
+            self.attributes[attribute] = (entry, check, )
             self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
             self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
             self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
             i += 1
+        self.mime_lists = {}
         for mime_list in GUIConfig.mime_lists:
             tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
             tree_model = tree.get_model()
@@ -308,12 +315,55 @@ class MimeTable(gtk.Table):
                     model.remove(selected)
             remove_button = gtk.Button('Remove')
             remove_button.connect('clicked', remove_from_tree, tree)
-            
+
+            self.mime_lists[mime_list] = tree_model
             self.attach(tree_scroll, 0, 2, i, i + 3)
             self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
             self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
             self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
             i += 3
+            
+    def clear(self):
+        for entry, check in self.attributes.itervalues():
+            entry.set_text('')
+            check.set_active(False)
+        for model in self.mime_lists.itervalues():
+            model.clear()
+
+    def save_changed(self, tree):
+        if self.last_selected is not None:
+            attributes = {}
+            for attribute in self.attributes:
+                entry, check = self.attributes[attribute]
+                attributes[attribute] = (entry.get_text(), check.get_active(), 
)
+            mime_lists = {}
+            for mime_list in GUIConfig.mime_lists:
+                container = mime_lists[mime_list] = []
+                model = self.mime_lists[mime_list]
+                i = model.iter_children(None)
+                while i is not None: # iterate over list elements
+                    container.append(model.get_value(i, 0))
+                    i = model.iter_next(i)
+            row = tree.get_model()[self.last_selected]
+            row[1] = attributes
+            row[2] = mime_lists
+
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+
+        self.clear()
+
+        self.last_selected = current = tree.get_selection().get_selected()[1]
+        row = tree.get_model()[current]
+        for attribute in row[1]:
+            entry, check = self.attributes[attribute]
+            entry.set_text(row[1][attribute][0])
+            check.set_active(row[1][attribute][1])
+        for mime_list in row[2]:
+            model = self.mime_lists[mime_list]
+            for element in row[2][mime_list]:
+                model.append((element, ))
+
 
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -456,6 +506,14 @@ class PyGTKControl():
         table, tree = self.tabs['unknown']
         tree.get_model().append(None, ('', '', False,True, '', False, {}, ))
 
+    def on_add_mime_type_menu_item_activate(self, widget):
+        '''Adds a new MIME type.'''
+        table, tree = self.tabs['MIME'][0]
+        mime_lists = {}
+        for mime_list in GUIConfig.mime_lists:
+            mime_lists[mime_list] = []
+        tree.get_model().append(('', {}, mime_lists, ))
+
     def on_new_menu_item_activate(self, widget = None):
         '''Clears configuration.'''
         if widget is not None:
@@ -583,8 +641,9 @@ class PyGTKControl():
         panels = gtk.HPaned()
         tree = MimeTreeView()
         panels.pack1(tree.scroll, True, False)
-        grid = MimeTable()
-        panels.pack2(grid, False, False)
+        table = MimeTable(tree)
+        panels.pack2(table, False, False)
+        self.tabs['MIME'] = (table, tree, )
 
         vpanels.pack1(panels)
 
@@ -593,6 +652,7 @@ class PyGTKControl():
         panels.pack1(tree.scroll, True, False)
         table = EditionTable(tree)
         panels.pack2(table, False, False)
+        self.tabs['MIME'] = (self.tabs['MIME'], (table, tree, ))
 
         vpanels.pack2(panels)
         
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 1766644..6806292 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -164,6 +164,32 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkMenuItem" id="mime_types_menu_item">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_MIME 
Types</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="mime_types_menu">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="add_mime_type_menu_item">
+                        <property name="label">Add MIME type</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_add_mime_type_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="add_mime_type_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-add</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>



commit 80d6cb01d33e598f0ec1fa7f45cd195794a5c112
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 23 22:01:02 2009 +0200

    GUI shows all mime options.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 100d2ab..fb401fc 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -196,6 +196,125 @@ class EditionTable(gtk.Table):
         model, selected = tree.get_selection().get_selected()
         return selected
 
+class DefinitionTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.TreeStore(
+                gobject.TYPE_STRING, # option name
+                gobject.TYPE_STRING, # option tooltip
+                gobject.TYPE_BOOLEAN, # True if option is known
+                gobject.TYPE_BOOLEAN, # enabled
+                gobject.TYPE_STRING, # value
+                gobject.TYPE_BOOLEAN, # value_check
+                gobject.TYPE_PYOBJECT)) # attributes dict
+        model = self.get_model()
+        def name_edited_handler(cell, path, text, data):
+            model = data
+            row = model[path]
+            if not row[2]: # don't edit names of known options
+                row[0] = text
+        name_renderer = gtk.CellRendererText()
+        name_renderer.set_property('editable', True)
+        name_renderer.connect('edited', name_edited_handler, model)
+        name_column = gtk.TreeViewColumn('name')
+        name_column.pack_start(name_renderer)
+        name_column.add_attribute(name_renderer, 'text', 0)
+        self.append_column(name_column)
+        value_renderer = gtk.CellRendererText()
+        value_column = gtk.TreeViewColumn('value')
+        value_column.pack_start(value_renderer)
+        value_column.add_attribute(value_renderer, 'text', 4)
+        self.append_column(value_column)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+        def show_tooltip(widget, x, y, keyboard_tip, tooltip):
+            if not widget.get_tooltip_context(x, y, keyboard_tip):
+                return False
+            else:
+                model, path, it = widget.get_tooltip_context(x, y, 
keyboard_tip)
+                tooltip.set_text(model[it][1])
+                widget.set_tooltip_row(tooltip, path)
+                return True
+        self.props.has_tooltip = True
+        self.connect("query-tooltip", show_tooltip)
+
+class MimeTreeView(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self, gtk.TreeStore(
+                gobject.TYPE_STRING, # mime name
+                gobject.TYPE_PYOBJECT, # mime single attributes
+                gobject.TYPE_PYOBJECT)) # mime attribute lists
+        model = self.get_model()
+        def mime_edited_handler(cell, path, text, data):
+            model = data
+            model[path][0] = text
+        mime_renderer = gtk.CellRendererText()
+        mime_renderer.set_property('editable', True)
+        mime_renderer.connect('edited', mime_edited_handler, model)
+        mime_column = gtk.TreeViewColumn('MIME Type')
+        mime_column.pack_start(mime_renderer)
+        mime_column.add_attribute(mime_renderer, 'text', 0)
+        self.append_column(mime_column)
+
+        self.scroll = gtk.ScrolledWindow()
+        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scroll.set_shadow_type(gtk.SHADOW_OUT)
+        self.scroll.set_border_width(5)
+        self.scroll.add(self)
+
+class MimeTable(gtk.Table):
+    def __init__(self):
+        gtk.Table.__init__(self, len(GUIConfig.mime_attributes) +
+                           3 * len(GUIConfig.mime_lists), 4)
+        i = 0
+        for attribute in GUIConfig.mime_attributes:
+            label = gtk.Label(attribute)
+            entry = gtk.Entry()
+            check = gtk.CheckButton()
+            self.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            self.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            i += 1
+        for mime_list in GUIConfig.mime_lists:
+            tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
+            tree_model = tree.get_model()
+            def tree_edited_handler(cell, path, text, data):
+                model = data
+                model[path][0] = text
+            tree_renderer = gtk.CellRendererText()
+            tree_renderer.set_property('editable', True)
+            tree_renderer.connect('edited', tree_edited_handler, tree_model)
+            tree_column = gtk.TreeViewColumn(mime_list)
+            tree_column.pack_start(tree_renderer)
+            tree_column.add_attribute(tree_renderer, 'text', 0)
+            tree.append_column(tree_column)
+            tree_scroll = gtk.ScrolledWindow()
+            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+            tree_scroll.set_border_width(5)
+            tree_scroll.add(tree)
+
+            def add_to_tree(button, model):
+                model.append(('', ))
+            add_button = gtk.Button('Add')
+            add_button.connect('clicked', add_to_tree, tree_model)
+            def remove_from_tree(button, tree):
+                model, selected = tree.get_selection().get_selected()
+                if selected is not None:
+                    model.remove(selected)
+            remove_button = gtk.Button('Remove')
+            remove_button.connect('clicked', remove_from_tree, tree)
+            
+            self.attach(tree_scroll, 0, 2, i, i + 3)
+            self.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            self.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
+            self.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            i += 3
+
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
 
@@ -436,56 +555,14 @@ class PyGTKControl():
             options = segregated_options.get(tab, [])
             panels = gtk.HPaned()
 
-            tree = gtk.TreeView(gtk.TreeStore(
-                    gobject.TYPE_STRING, # option name
-                    gobject.TYPE_STRING, # option tooltip
-                    gobject.TYPE_BOOLEAN, # True if option is known
-                    gobject.TYPE_BOOLEAN, # enabled
-                    gobject.TYPE_STRING, # value
-                    gobject.TYPE_BOOLEAN, # value_check
-                    gobject.TYPE_PYOBJECT)) # attributes dict
+            tree = DefinitionTreeView()
             tree_model = tree.get_model()
-            def tree_name_edited_handler(cell, path, text, data):
-                model = data
-                row = model[path]
-                if not row[2]: # don't edit names of known options
-                    row[0] = text
-            tree_name_renderer = gtk.CellRendererText()
-            tree_name_renderer.set_property('editable', True)
-            tree_name_renderer.connect('edited', tree_name_edited_handler,
-                                       tree_model)
-            tree_name_column = gtk.TreeViewColumn('name')
-            tree_name_column.pack_start(tree_name_renderer)
-            tree_name_column.add_attribute(tree_name_renderer, 'text', 0)
-            tree.append_column(tree_name_column)
-            tree_value_renderer = gtk.CellRendererText()
-            tree_value_column = gtk.TreeViewColumn('value')
-            tree_value_column.pack_start(tree_value_renderer)
-            tree_value_column.add_attribute(tree_value_renderer, 'text', 4)
-            tree.append_column(tree_value_column)
-
-            tree_scroll = gtk.ScrolledWindow()
-            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
-            tree_scroll.set_border_width(5)
-            tree_scroll.add(tree)
-            panels.pack1(tree_scroll, True, False)
+            panels.pack1(tree.scroll, True, False)
             table = EditionTable(tree)
             panels.pack2(table, False, False)
 
             self.tabs[tab] = (table, tree, )
 
-            def show_tooltip(widget, x, y, keyboard_tip, tooltip):
-                if not widget.get_tooltip_context(x, y, keyboard_tip):
-                    return False
-                else:
-                    model, path, it = widget.get_tooltip_context(x, y, 
keyboard_tip)
-                    tooltip.set_text(model[it][1])
-                    widget.set_tooltip_row(tooltip, path)
-                    return True
-            tree.props.has_tooltip = True
-            tree.connect("query-tooltip", show_tooltip)
-
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
                 # all but first three columns will be set to defaults later by
@@ -501,83 +578,26 @@ class PyGTKControl():
 
     def construct_mime(self):
         '''Reads mime options from file and prepares GUI.'''
-        panels = gtk.HPaned()
+        vpanels = gtk.VPaned()
         
-        tree = gtk.TreeView(gtk.TreeStore(
-                gobject.TYPE_STRING, # mime name
-                gobject.TYPE_PYOBJECT, # mime single attributes
-                gobject.TYPE_PYOBJECT)) # mime attribute lists
-        tree_model = tree.get_model()
-        def tree_mime_edited_handler(cell, path, text, data):
-            model = data
-            model[path][0] = text
-        tree_mime_renderer = gtk.CellRendererText()
-        tree_mime_renderer.set_property('editable', True)
-        tree_mime_renderer.connect('edited', tree_mime_edited_handler,
-                                   tree_model)
-        tree_mime_column = gtk.TreeViewColumn('MIME Type')
-        tree_mime_column.pack_start(tree_mime_renderer)
-        tree_mime_column.add_attribute(tree_mime_renderer, 'text', 0)
-        tree.append_column(tree_mime_column)
-
-        tree_scroll = gtk.ScrolledWindow()
-        tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
-        tree_scroll.set_border_width(5)
-        tree_scroll.add(tree)
-
-        panels.pack1(tree_scroll, True, False)
-
-        grid = gtk.Table(len(GUIConfig.mime_attributes) +
-                         3 * len(GUIConfig.mime_lists), 4)
-        i = 0
-        for attribute in GUIConfig.mime_attributes:
-            label = gtk.Label(attribute)
-            entry = gtk.Entry()
-            check = gtk.CheckButton()
-            grid.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
-            grid.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
-            grid.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
-            i += 1
-        for mime_list in GUIConfig.mime_lists:
-            tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
-            tree_model = tree.get_model()
-            def tree_edited_handler(cell, path, text, data):
-                model = data
-                model[path][0] = text
-            tree_renderer = gtk.CellRendererText()
-            tree_renderer.set_property('editable', True)
-            tree_renderer.connect('edited', tree_edited_handler, tree_model)
-            tree_column = gtk.TreeViewColumn(mime_list)
-            tree_column.pack_start(tree_renderer)
-            tree_column.add_attribute(tree_renderer, 'text', 0)
-            tree.append_column(tree_column)
-            tree_scroll = gtk.ScrolledWindow()
-            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
-            tree_scroll.set_border_width(5)
-            tree_scroll.add(tree)
+        panels = gtk.HPaned()
+        tree = MimeTreeView()
+        panels.pack1(tree.scroll, True, False)
+        grid = MimeTable()
+        panels.pack2(grid, False, False)
 
-            def add_to_tree(button, model):
-                model.append(('', ))
-            add_button = gtk.Button('Add')
-            add_button.connect('clicked', add_to_tree, tree_model)
-            def remove_from_tree(button, tree):
-                model, selected = tree.get_selection().get_selected()
-                if selected is not None:
-                    model.remove(selected)
-            remove_button = gtk.Button('Remove')
-            remove_button.connect('clicked', remove_from_tree, tree)
-            grid.attach(tree_scroll, 0, 2, i, i + 3)
-            grid.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
-            grid.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
-            grid.attach(gtk.Label(), 2, 3, i + 2, i + 3)
-            i += 3
+        vpanels.pack1(panels)
 
-        panels.pack2(grid, False, False)
+        panels = gtk.HPaned()
+        tree = DefinitionTreeView()
+        panels.pack1(tree.scroll, True, False)
+        table = EditionTable(tree)
+        panels.pack2(table, False, False)
 
+        vpanels.pack2(panels)
+        
         self.widgets.get_widget('notebook').append_page(
-            panels, gtk.Label('MIME Type'))
+            vpanels, gtk.Label('MIME Type'))
 
         self.widgets.get_widget('notebook').show_all()
 



commit 8f2065d32f68cbb7d6b5584739fadba1e23aefee
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 22 22:30:56 2009 +0200

    GUI widgets for mime types.

diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
index be20e77..c51f5f6 100644
--- a/misc/PyGTK_Control/GUIConfig.py
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -67,3 +67,8 @@ options['symlinks.follow'] = ('Define if links should be 
followed.', 'bool', )
 
 # don't put 'other' or 'unknown' here
 tabs = ['server', 'control', 'ftp', 'http', 'log_color']
+
+# MIME types
+mime_name = 'mime'
+mime_attributes = ['handler', 'self_executed', 'param', 'path']
+mime_lists = ['extensions', 'filters']
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 77c2de2..100d2ab 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -204,6 +204,7 @@ class PyGTKControl():
         self.widgets = gtk.glade.XML(self.gladefile, 'window')
         self.widgets.signal_autoconnect(self)
         self.construct_options()
+        self.construct_mime()
         self.chooser = None # Active file chooser
         self.path = None # path of currently edited file
         self.controller = None
@@ -498,5 +499,87 @@ class PyGTKControl():
         self.on_new_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 
+    def construct_mime(self):
+        '''Reads mime options from file and prepares GUI.'''
+        panels = gtk.HPaned()
+        
+        tree = gtk.TreeView(gtk.TreeStore(
+                gobject.TYPE_STRING, # mime name
+                gobject.TYPE_PYOBJECT, # mime single attributes
+                gobject.TYPE_PYOBJECT)) # mime attribute lists
+        tree_model = tree.get_model()
+        def tree_mime_edited_handler(cell, path, text, data):
+            model = data
+            model[path][0] = text
+        tree_mime_renderer = gtk.CellRendererText()
+        tree_mime_renderer.set_property('editable', True)
+        tree_mime_renderer.connect('edited', tree_mime_edited_handler,
+                                   tree_model)
+        tree_mime_column = gtk.TreeViewColumn('MIME Type')
+        tree_mime_column.pack_start(tree_mime_renderer)
+        tree_mime_column.add_attribute(tree_mime_renderer, 'text', 0)
+        tree.append_column(tree_mime_column)
+
+        tree_scroll = gtk.ScrolledWindow()
+        tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+        tree_scroll.set_border_width(5)
+        tree_scroll.add(tree)
+
+        panels.pack1(tree_scroll, True, False)
+
+        grid = gtk.Table(len(GUIConfig.mime_attributes) +
+                         3 * len(GUIConfig.mime_lists), 4)
+        i = 0
+        for attribute in GUIConfig.mime_attributes:
+            label = gtk.Label(attribute)
+            entry = gtk.Entry()
+            check = gtk.CheckButton()
+            grid.attach(label, 0, 1, i, i + 1, yoptions = gtk.FILL)
+            grid.attach(entry, 1, 3, i, i + 1, yoptions = gtk.FILL)
+            grid.attach(check, 3, 4, i, i + 1, gtk.FILL, gtk.FILL)
+            i += 1
+        for mime_list in GUIConfig.mime_lists:
+            tree = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
+            tree_model = tree.get_model()
+            def tree_edited_handler(cell, path, text, data):
+                model = data
+                model[path][0] = text
+            tree_renderer = gtk.CellRendererText()
+            tree_renderer.set_property('editable', True)
+            tree_renderer.connect('edited', tree_edited_handler, tree_model)
+            tree_column = gtk.TreeViewColumn(mime_list)
+            tree_column.pack_start(tree_renderer)
+            tree_column.add_attribute(tree_renderer, 'text', 0)
+            tree.append_column(tree_column)
+            tree_scroll = gtk.ScrolledWindow()
+            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+            tree_scroll.set_border_width(5)
+            tree_scroll.add(tree)
+
+            def add_to_tree(button, model):
+                model.append(('', ))
+            add_button = gtk.Button('Add')
+            add_button.connect('clicked', add_to_tree, tree_model)
+            def remove_from_tree(button, tree):
+                model, selected = tree.get_selection().get_selected()
+                if selected is not None:
+                    model.remove(selected)
+            remove_button = gtk.Button('Remove')
+            remove_button.connect('clicked', remove_from_tree, tree)
+            grid.attach(tree_scroll, 0, 2, i, i + 3)
+            grid.attach(add_button, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            grid.attach(remove_button, 2, 3, i + 1, i + 2, yoptions = gtk.FILL)
+            grid.attach(gtk.Label(), 2, 3, i + 2, i + 3)
+            i += 3
+
+        panels.pack2(grid, False, False)
+
+        self.widgets.get_widget('notebook').append_page(
+            panels, gtk.Label('MIME Type'))
+
+        self.widgets.get_widget('notebook').show_all()
+
 PyGTKControl()
 gtk.main()



commit fd58fa5679660d48b52cf4ee0d31e963dabc1b40
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 22 20:50:43 2009 +0200

    Removed old code.

diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index ba25a6b..017368d 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -33,17 +33,7 @@ class MIMETypeTest(unittest.TestCase):
                 'http.default_file',
                 [DefinitionElement(attributes = {'value': 'index.html'}),
                  DefinitionElement(attributes = {'value': 'index.htm'})]))
-#        self.text = \
-#             '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
-#   <EXTENSION value="xhtml" />
-#   <EXTENSION value="xml" />
-#   <EXTENSION value="py" />
-#   <FILTER value="gzip" />
-#   <FILTER value="bzip2" />
-#   {0}
-#   <PATH regex="^/cgi-bin/python/.*$" />
-# </MIME>'''.format('\n'.join(map(lambda element: str(element),
-#                                 self.definitions)))
+
     def test_creation(self):
         MIMEType('text/plain', 'CGI')
         MIMEType('text/plain', 'CGI', '/usr/bin/python')



commit 1cc305c0cd2977b99bcd6dcbad4c97f9b51bb7d9
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 22 00:11:51 2009 +0200

    Value field is auto disabled for definition trees.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index b26a9d6..77c2de2 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -79,9 +79,15 @@ class EditionTable(gtk.Table):
         self.value_check_field = value_checkbutton = gtk.CheckButton()
         value_checkbutton.set_tooltip_text('If active value will be used.')
         value_checkbutton.set_active(True)
-        value_checkbutton.connect(
-            'toggled',
-            lambda button: value_entry.set_editable(button.get_active()))
+        def toggle_value(button, tree):
+            # don't allow value for definition with children
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None and model.iter_children(selected) is not 
None:
+                button.set_active(False)
+                return
+            active = button.get_active()
+            self.value_field.set_editable(active)
+        value_checkbutton.connect('toggled', toggle_value, tree)
         self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
@@ -91,6 +97,8 @@ class EditionTable(gtk.Table):
             model, selected = tree.get_selection().get_selected()
             if selected is not None:
                 model.append(selected, ('', '', False, True, '', False, {}, ))
+                self.value_check_field.set_active(False) # auto disable value
+                self.value_field.set_editable(False)
         add_definition_button.connect('clicked', add_sub_definition)
         self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
 



commit f9d4639ef55171aadf5fecfd236c5905b6e4c007
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 15:12:12 2009 +0200

    User can add unknown definitions.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 2bc8ac3..b26a9d6 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -323,6 +323,11 @@ class PyGTKControl():
                 i = model.iter_next(i)
         return MyServerConfig(definitions)
 
+    def on_add_unknown_definition_menu_item_activate(self, widget):
+        '''Adds a new definition to unknown tab.'''
+        table, tree = self.tabs['unknown']
+        tree.get_model().append(None, ('', '', False,True, '', False, {}, ))
+
     def on_new_menu_item_activate(self, widget = None):
         '''Clears configuration.'''
         if widget is not None:
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index b426812..1766644 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -138,6 +138,32 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkMenuItem" id="definitions_menu_item">
+                <property name="visible">True</property>
+                <property name="label" 
translatable="yes">_Definitions</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="definitions_menu">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="add_unknown_definition_menu_item">
+                        <property name="label">Add unknown 
definition</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_add_unknown_definition_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" 
id="add_unknown_definition_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-add</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>



commit 7c1da124afaac94910e59244fa1b01f0504a7e5c
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 14:55:08 2009 +0200

    Name editing.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 502ef8a..2bc8ac3 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -431,14 +431,23 @@ class PyGTKControl():
                     gobject.TYPE_BOOLEAN, # value_check
                     gobject.TYPE_PYOBJECT)) # attributes dict
             tree_model = tree.get_model()
-            tree_renderer = gtk.CellRendererText()
+            def tree_name_edited_handler(cell, path, text, data):
+                model = data
+                row = model[path]
+                if not row[2]: # don't edit names of known options
+                    row[0] = text
+            tree_name_renderer = gtk.CellRendererText()
+            tree_name_renderer.set_property('editable', True)
+            tree_name_renderer.connect('edited', tree_name_edited_handler,
+                                       tree_model)
             tree_name_column = gtk.TreeViewColumn('name')
-            tree_name_column.pack_start(tree_renderer)
-            tree_name_column.add_attribute(tree_renderer, 'text', 0)
+            tree_name_column.pack_start(tree_name_renderer)
+            tree_name_column.add_attribute(tree_name_renderer, 'text', 0)
             tree.append_column(tree_name_column)
+            tree_value_renderer = gtk.CellRendererText()
             tree_value_column = gtk.TreeViewColumn('value')
-            tree_value_column.pack_start(tree_renderer)
-            tree_value_column.add_attribute(tree_renderer, 'text', 4)
+            tree_value_column.pack_start(tree_value_renderer)
+            tree_value_column.add_attribute(tree_value_renderer, 'text', 4)
             tree.append_column(tree_value_column)
 
             tree_scroll = gtk.ScrolledWindow()



commit 3ff6dcf1f152b1961df5f9f4da1c4213428b194a
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 14:36:22 2009 +0200

    Added value column to TreeView.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 2b9d21a..502ef8a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -431,12 +431,15 @@ class PyGTKControl():
                     gobject.TYPE_BOOLEAN, # value_check
                     gobject.TYPE_PYOBJECT)) # attributes dict
             tree_model = tree.get_model()
-            tree.set_headers_visible(False)
-            tree_column = gtk.TreeViewColumn()
             tree_renderer = gtk.CellRendererText()
-            tree_column.pack_start(tree_renderer)
-            tree_column.add_attribute(tree_renderer, 'text', 0)
-            tree.append_column(tree_column)
+            tree_name_column = gtk.TreeViewColumn('name')
+            tree_name_column.pack_start(tree_renderer)
+            tree_name_column.add_attribute(tree_renderer, 'text', 0)
+            tree.append_column(tree_name_column)
+            tree_value_column = gtk.TreeViewColumn('value')
+            tree_value_column.pack_start(tree_renderer)
+            tree_value_column.add_attribute(tree_renderer, 'text', 4)
+            tree.append_column(tree_value_column)
 
             tree_scroll = gtk.ScrolledWindow()
             tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)



commit 5de9a2c8d6f8121831dc8752a14fcc932bcb43e5
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 14:31:03 2009 +0200

    Add/Remove definitions are handled now.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index dd1b1d6..2b9d21a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -87,9 +87,20 @@ class EditionTable(gtk.Table):
         self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
         add_definition_button = gtk.Button('add sub-definition')
+        def add_sub_definition(button):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None:
+                model.append(selected, ('', '', False, True, '', False, {}, ))
+        add_definition_button.connect('clicked', add_sub_definition)
         self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
 
         remove_definition_button = gtk.Button('remove this definition')
+        def remove_definition(button):
+            model, selected = tree.get_selection().get_selected()
+            if selected is not None and not model[selected][2]:
+                model.remove(selected)
+                self.clear()
+        remove_definition_button.connect('clicked', remove_definition)
         self.attach(remove_definition_button, 0, 3, 3, 4, yoptions = gtk.FILL)
 
         add_attribute_button = gtk.Button('add attribute')
@@ -155,10 +166,10 @@ class EditionTable(gtk.Table):
                     self.attributes_field.get_value(i, 1)
                 i = self.attributes_field.iter_next(i)
             row = tree.get_model()[self.last_selected]
-            row[2] = self.enabled_field.get_active()
-            row[3] = self.value_field.get_text()
-            row[4] = self.value_check_field.get_active()
-            row[5] = attributes
+            row[3] = self.enabled_field.get_active()
+            row[4] = self.value_field.get_text()
+            row[5] = self.value_check_field.get_active()
+            row[6] = attributes
 
     def cursor_changed(self, tree):
         self.save_changed(tree)
@@ -167,10 +178,10 @@ class EditionTable(gtk.Table):
 
         self.last_selected = current = self.get_selected(tree)
         row = tree.get_model()[current]
-        self.enabled_field.set_active(row[2])
-        self.value_field.set_text(row[3])
-        self.value_check_field.set_active(row[4])
-        for attribute in row[5]:
+        self.enabled_field.set_active(row[3])
+        self.value_field.set_text(row[4])
+        self.value_check_field.set_active(row[5])
+        for attribute in row[6]:
             self.attributes_field.append((attribute, row[5][attribute], ))
 
     def get_selected(self, tree):
@@ -279,10 +290,10 @@ class PyGTKControl():
         def make_def(current, model):
             row = model[current]
             name = row[0]
-            enabled = row[2]
-            value = row[3]
-            value_check = row[4]
-            attributes = row[5]
+            enabled = row[3]
+            value = row[4]
+            value_check = row[5]
+            attributes = row[6]
             if value_check:
                 attributes['value'] = value
             if not enabled:
@@ -316,6 +327,8 @@ class PyGTKControl():
         '''Clears configuration.'''
         if widget is not None:
             self.path = None
+        table, tree = self.tabs['unknown']
+        tree.get_model().clear()
         for tab in self.tabs:
             table, tree = self.tabs[tab]
             model = tree.get_model()
@@ -324,10 +337,10 @@ class PyGTKControl():
             i = model.iter_children(None)
             while i is not None: # iterate over options
                 row = model[i]
-                row[2] = False
-                row[3] = ''
-                row[4] = False
-                row[5] = {}
+                row[3] = False
+                row[4] = ''
+                row[5] = False
+                row[6] = {}
                 child = model.iter_children(i)
                 if child is not None: # remove node children
                     while model.remove(child):
@@ -357,7 +370,8 @@ class PyGTKControl():
                         get_value_and_attributes(d)
                     i = model.append(
                         parent,
-                        (name, '', enabled, value, value_check, attributes, ))
+                        (name, '', False, enabled, value, value_check,
+                         attributes, ))
                     add_children(i, d, model)
 
         self.on_new_menu_item_activate()
@@ -377,12 +391,13 @@ class PyGTKControl():
                 while model[i][0] != name:
                     i = model.iter_next(i)
             else:
-                i = model.append(None, (name, '', False, '', False, {}, ))
+                i = model.append(None,
+                                 (name, '', False, False, '', False, {}, ))
             row = model[i]
-            row[2] = enabled
-            row[3] = value
-            row[4] = value_check
-            row[5] = attributes
+            row[3] = enabled
+            row[4] = value
+            row[5] = value_check
+            row[6] = attributes
             add_children(i, definition, model)
 
     def construct_options(self):
@@ -410,6 +425,7 @@ class PyGTKControl():
             tree = gtk.TreeView(gtk.TreeStore(
                     gobject.TYPE_STRING, # option name
                     gobject.TYPE_STRING, # option tooltip
+                    gobject.TYPE_BOOLEAN, # True if option is known
                     gobject.TYPE_BOOLEAN, # enabled
                     gobject.TYPE_STRING, # value
                     gobject.TYPE_BOOLEAN, # value_check
@@ -446,9 +462,9 @@ class PyGTKControl():
 
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
-                # all but first two columns will be set to defaults later by
+                # all but first three columns will be set to defaults later by
                 # on_new_menu_item_activate
-                tree_model.append(None, (option, tooltip_text, False, '',
+                tree_model.append(None, (option, tooltip_text, True, False, '',
                                          False, {}, ))
 
             self.widgets.get_widget('notebook').append_page(



commit e88e3a334dfb8c841dfbd9f7cb21511fac6c18f5
Merge: 9b1a0c5 d083c2b
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 12:16:50 2009 +0200

    Merge branch 'treeview'




commit d083c2b87c819b698923635febfcd83e841fe402
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 12:15:16 2009 +0200

    Added tooltips.

diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
index 60edc00..be20e77 100644
--- a/misc/PyGTK_Control/GUIConfig.py
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -45,7 +45,7 @@ options['server.max_threads'] = ('Maximum number of serving 
threads that the sch
 options['server.buffer_size'] = ('Dimension of every buffer in bytes.', 
'integer', )
 options['server.max_connections'] = ('Define the max number of connections to 
allow to server. 0 means allow infinite connections.', 'integer', )
 options['server.max_accepted_connections'] = ('Define max number of 
connections to accept.', 'integer', )
-options['server.max_log_size'] = ('max size of the log file in bytes.', 
'integer', )
+options['server.max_log_size'] = ('Max size of the log file in bytes.', 
'integer', )
 options['server.admin'] = ('Administrator e-mail.', 'string', )
 options['server.max_files_cache'] = ('Max cache size for static files.', 
'integer', )
 options['server.max_file_cache'] = ('Max cache size for single static file.', 
'integer', )
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index f047aa2..dd1b1d6 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -45,10 +45,10 @@ class Connection():
     def destroy(self):
         '''Destroys this widget.'''
         self.widgets.get_widget('connectiondialog').destroy()
-        
+
     def on_cancel_button_clicked(self, widget):
         self.destroy()
-    
+
     def get_host(self):
         return self.widgets.get_widget('host_entry').get_text()
 
@@ -64,7 +64,7 @@ class Connection():
 class EditionTable(gtk.Table):
     def __init__(self, tree):
         gtk.Table.__init__(self, 8, 3)
-        
+
         tree.connect('cursor-changed', self.cursor_changed)
         self.last_selected = None
 
@@ -88,7 +88,7 @@ class EditionTable(gtk.Table):
 
         add_definition_button = gtk.Button('add sub-definition')
         self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
-        
+
         remove_definition_button = gtk.Button('remove this definition')
         self.attach(remove_definition_button, 0, 3, 3, 4, yoptions = gtk.FILL)
 
@@ -125,7 +125,7 @@ class EditionTable(gtk.Table):
                                   (attributes_model, 0, ))
         variable_column.pack_start(variable_renderer)
         variable_column.add_attribute(variable_renderer, 'text', 0)
-        
+
         value_column = gtk.TreeViewColumn('value')
         value_renderer = gtk.CellRendererText()
         value_renderer.set_property('editable', True)
@@ -133,7 +133,7 @@ class EditionTable(gtk.Table):
                                   (attributes_model, 1, ))
         value_column.pack_start(value_renderer)
         value_column.add_attribute(value_renderer, 'text', 1)
-        
+
         attributes_list.append_column(variable_column)
         attributes_list.append_column(value_column)
         self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
@@ -159,12 +159,12 @@ class EditionTable(gtk.Table):
             row[3] = self.value_field.get_text()
             row[4] = self.value_check_field.get_active()
             row[5] = attributes
-            
+
     def cursor_changed(self, tree):
         self.save_changed(tree)
-            
+
         self.clear()
-        
+
         self.last_selected = current = self.get_selected(tree)
         row = tree.get_model()[current]
         self.enabled_field.set_active(row[2])
@@ -176,7 +176,7 @@ class EditionTable(gtk.Table):
     def get_selected(self, tree):
         model, selected = tree.get_selection().get_selected()
         return selected
-    
+
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
 
@@ -272,7 +272,7 @@ class PyGTKControl():
             config = self.get_current_config()
             with open(self.path, 'w') as f:
                 f.write(str(config))
-    
+
     def get_current_config(self):
         '''Returns current configuration as MyServerConfig instance.'''
 
@@ -298,7 +298,7 @@ class PyGTKControl():
                         definitions.append(definition)
                     i = model.iter_next(i)
                 return DefinitionTree(name, definitions, attributes)
-            
+
         definitions = []
         for tab in self.tabs:
             table, tree = self.tabs[tab]
@@ -336,7 +336,7 @@ class PyGTKControl():
 
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
-        
+
         def get_value_and_attributes(definition):
             name = definition.get_name()
             enabled = True
@@ -359,17 +359,17 @@ class PyGTKControl():
                         parent,
                         (name, '', enabled, value, value_check, attributes, ))
                     add_children(i, d, model)
-        
+
         self.on_new_menu_item_activate()
         for definition in config.get_definitions():
             name, enabled, value, value_check, attributes = \
                 get_value_and_attributes(definition)
-            
+
             if name not in self.options:
                 tab_name = 'unknown'
             else:
                 tab_name = self.options[name]
-            
+
             tree = self.tabs[tab_name][1]
             model = tree.get_model()
             if tab_name != 'unknown':
@@ -406,7 +406,7 @@ class PyGTKControl():
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             options = segregated_options.get(tab, [])
             panels = gtk.HPaned()
-            
+
             tree = gtk.TreeView(gtk.TreeStore(
                     gobject.TYPE_STRING, # option name
                     gobject.TYPE_STRING, # option tooltip
@@ -421,7 +421,7 @@ class PyGTKControl():
             tree_column.pack_start(tree_renderer)
             tree_column.add_attribute(tree_renderer, 'text', 0)
             tree.append_column(tree_column)
-            
+
             tree_scroll = gtk.ScrolledWindow()
             tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
             tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
@@ -432,14 +432,25 @@ class PyGTKControl():
             panels.pack2(table, False, False)
 
             self.tabs[tab] = (table, tree, )
-            
+
+            def show_tooltip(widget, x, y, keyboard_tip, tooltip):
+                if not widget.get_tooltip_context(x, y, keyboard_tip):
+                    return False
+                else:
+                    model, path, it = widget.get_tooltip_context(x, y, 
keyboard_tip)
+                    tooltip.set_text(model[it][1])
+                    widget.set_tooltip_row(tooltip, path)
+                    return True
+            tree.props.has_tooltip = True
+            tree.connect("query-tooltip", show_tooltip)
+
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
                 # all but first two columns will be set to defaults later by
                 # on_new_menu_item_activate
                 tree_model.append(None, (option, tooltip_text, False, '',
                                          False, {}, ))
-             
+
             self.widgets.get_widget('notebook').append_page(
                 panels, gtk.Label(tab))
 



commit f9ecb02a7fe208e42c7b26cecd809dffd15e60ac
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 12:08:05 2009 +0200

    Use gtk.HPaned instead of gtk.HBox.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 7041a10..f047aa2 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -405,7 +405,7 @@ class PyGTKControl():
         self.tabs = {} # tab name => (table, tree, )
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             options = segregated_options.get(tab, [])
-            panels = gtk.HBox()
+            panels = gtk.HPaned()
             
             tree = gtk.TreeView(gtk.TreeStore(
                     gobject.TYPE_STRING, # option name
@@ -427,9 +427,9 @@ class PyGTKControl():
             tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
             tree_scroll.set_border_width(5)
             tree_scroll.add(tree)
-            panels.pack_start(tree_scroll)
+            panels.pack1(tree_scroll, True, False)
             table = EditionTable(tree)
-            panels.pack_start(table)
+            panels.pack2(table, False, False)
 
             self.tabs[tab] = (table, tree, )
             



commit 5eb9ceee5cb46677d0ed82d0b99ca40deec9fa27
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 11:53:13 2009 +0200

    Unknown options are handled as they should be.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index ca4ce47..7041a10 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -337,9 +337,6 @@ class PyGTKControl():
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
         
-        def put_to_unknown(definition):
-            print('Unknown option:', definition, sep = '\n')
-            
         def get_value_and_attributes(definition):
             name = definition.get_name()
             enabled = True
@@ -369,14 +366,18 @@ class PyGTKControl():
                 get_value_and_attributes(definition)
             
             if name not in self.options:
-                put_to_unknown(definition)
-                continue
+                tab_name = 'unknown'
+            else:
+                tab_name = self.options[name]
             
-            tree = self.tabs[self.options[name]][1]
+            tree = self.tabs[tab_name][1]
             model = tree.get_model()
-            i = model.iter_children(None)
-            while model[i][0] != name:
-                i = model.iter_next(i)
+            if tab_name != 'unknown':
+                i = model.iter_children(None) # find this option
+                while model[i][0] != name:
+                    i = model.iter_next(i)
+            else:
+                i = model.append(None, (name, '', False, '', False, {}, ))
             row = model[i]
             row[2] = enabled
             row[3] = value



commit 4db2041c0917463789b51cd594ee24d6ced2ad35
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 21 11:40:28 2009 +0200

    Implemented save file.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index c7f5f9b..ca4ce47 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -274,36 +274,43 @@ class PyGTKControl():
                 f.write(str(config))
     
     def get_current_config(self):
-        '''Returns current configuration as list of definitions.'''
-        definitions = []
-        for option in self.options:
-            check, field, var = self.options[option]
-            if not check.get_active():
-                continue
-            definition = self.definitions.get(option)
-            if var == 'string':
-                definition.set_attribute('value', field.get_text())
-            elif var == 'integer':
-                definition.set_attribute('value', str(int(field.get_value())))
-            elif var == 'bool':
-                value = 'NO' if field.get_active() else 'YES'
-                definition.set_attribute('value', value)
-            elif var == 'list':
-                values = []
-                model = field.get_model()
-                i = model.iter_children(None)
-                while i is not None:
-                    values.append(model.get_value(i, 0))
+        '''Returns current configuration as MyServerConfig instance.'''
+
+        def make_def(current, model):
+            row = model[current]
+            name = row[0]
+            enabled = row[2]
+            value = row[3]
+            value_check = row[4]
+            attributes = row[5]
+            if value_check:
+                attributes['value'] = value
+            if not enabled:
+                return None
+            i = model.iter_children(current)
+            if i is None:
+                return DefinitionElement(name, attributes)
+            else:
+                definitions = []
+                while i is not None: # iterate over children
+                    definition = make_def(i, model)
+                    if definition is not None:
+                        definitions.append(definition)
                     i = model.iter_next(i)
-                values = map(
-                    lambda v: DefinitionElement(attributes = {'value': v}),
-                    values)
-                for i in xrange(len(definition.get_definitions())):
-                    definition.remove_definition(0)
-                for value in values:
-                    definition.add_definition(value)
-            definitions.append(definition)
-        return MyServerConfig(definitions + self.unknown)
+                return DefinitionTree(name, definitions, attributes)
+            
+        definitions = []
+        for tab in self.tabs:
+            table, tree = self.tabs[tab]
+            model = tree.get_model()
+            table.save_changed(tree)
+            i = model.iter_children(None)
+            while i is not None: # iterate over options
+                definition = make_def(i, model)
+                if definition is not None:
+                    definitions.append(definition)
+                i = model.iter_next(i)
+        return MyServerConfig(definitions)
 
     def on_new_menu_item_activate(self, widget = None):
         '''Clears configuration.'''



commit 8511f7bf10f2a7181186d4b8a63cd9c826378e85
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 23:29:06 2009 +0200

    Removed old GUI.

diff --git a/misc/PyGTK_Control/old/ConfigGUI.py 
b/misc/PyGTK_Control/old/ConfigGUI.py
deleted file mode 100755
index fe3a102..0000000
--- a/misc/PyGTK_Control/old/ConfigGUI.py
+++ /dev/null
@@ -1,372 +0,0 @@
-#!/usr/bin/env python
-
-# MyServer
-# Copyright (C) 2002, 2003, 2004, 2007, 2008 The MyServer Team
-# 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 gobject
-
-try:
-    import pygtk
-    pygtk.require("2.0")
-except:
-    pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
-
-class ConfigGUIGTK:
-    """@package ConfigGUIGTK
-    UI for configurtion tool"""
-
-    def __init__(self):
-
-        # Connect the Glade file
-        gladefile = "XMLGui.glade"
-        self.wTree = gtk.glade.XML(gladefile)
-
-        # Bind main window
-        self.window = self.wTree.get_widget("ConfigUI")
-        # Bind "destroy" event
-        if (self.window):
-            self.window.connect("destroy", gtk.main_quit)
-
-        # Set defult values on comboboxes
-        self.wTree.get_widget("cbVerbosityLevel").set_active(0)
-        self.wTree.get_widget("combobox2").set_active(0)
-
-        # Here we call the methods creating widgets with functionality
-        self.create_treeview_for_default_filenames()
-        self.create_treeview_for_host_names()
-        self.create_treeview_for_IP_addresses()
-        self.create_a_combobox_for_connection_types()
-        self.create_treeview_with_MIME_types()
-        self.bind_signals()
-
-
-    def create_treeview_for_default_filenames(self):
-        """ Create treeview widget structure (one column) for default 
filenames """
-        self.treeview=self.wTree.get_widget("treeview1")
-        self.treemodel=gtk.TreeStore(gobject.TYPE_STRING)
-        self.treeview.set_model(self.treemodel)
-        # Add treeview header
-        self.treeview.set_headers_visible(True)
-        renderer=gtk.CellRendererText()
-        column=gtk.TreeViewColumn("Default file names:",renderer, text=0)
-        column.set_resizable(True)
-        self.treeview.append_column(column)
-        renderer=gtk.CellRendererText()
-
-        # Add list items to tree view
-        self.insert_row(self.treemodel,None,'default.html')
-        self.insert_row(self.treemodel,None,'default.htm')
-        self.insert_row(self.treemodel,None,'default.php')
-        self.insert_row(self.treemodel,None,'index.html')
-        self.insert_row(self.treemodel,None,'index.htm')
-        self.insert_row(self.treemodel,None,'index.php')
-
-        self.treeview.show()
-
-    def create_treeview_for_host_names(self):
-        """ Create treeview widget structure (one column) for host names """
-        self.treeviewHost=self.wTree.get_widget("treeviewHost")
-        self.treemodelHost=gtk.TreeStore(gobject.TYPE_STRING)
-        self.treeviewHost.set_model(self.treemodelHost)
-
-        renderer=gtk.CellRendererText()
-        column=gtk.TreeViewColumn("Host:",renderer, text=0)
-        column.set_resizable(True)
-        self.treeviewHost.append_column(column)
-        renderer=gtk.CellRendererText()
-        self.treeviewHost.show()
-
-    def create_treeview_for_IP_addresses(self):
-        """ Create treeview widget structure (one column) for IP addresses """
-        self.treeviewIP=self.wTree.get_widget("treeviewIP")
-        self.treemodelIP=gtk.TreeStore(gobject.TYPE_STRING)
-        self.treeviewIP.set_model(self.treemodelIP)
-
-        renderer=gtk.CellRendererText()
-        column=gtk.TreeViewColumn("IP:",renderer, text=0)
-        column.set_resizable(True)
-        self.treeviewIP.append_column(column)
-        renderer=gtk.CellRendererText()
-        self.treeviewIP.show()
-
-    def create_a_combobox_for_connection_types(self):
-        """ Cretes a combobox with connection types """
-        self.store2 = gtk.ListStore(gobject.TYPE_STRING)
-        #populate
-        self.store2.append(["Every HTTP connection"])
-        self.store2.append(["FTP connection"])
-        self.cbHost=self.wTree.get_widget("combobox5")
-        self.cbHost.set_model(self.store2)
-        cell = gtk.CellRendererText()
-        self.cbHost.pack_start(cell, True)
-        self.cbHost.add_attribute(cell, 'text',0)
-
-    def create_treeview_with_MIME_types(self):
-        """ Add list items to tree view. The types are parsed
-        from xml file using helper class MIMEtpe
-        the result is used to poulate the treeview and the combobox """
-
-        from MIMEtypes import MIMEtype, ParseTypes
-        TYPES = ParseTypes("MIMEtypes.xml")
-
-        #Create combobox to select MIME type
-        # http://faq.pygtk.org/index.py?req=show&file=faq16.008.htp
-        self.comboboxMIME = self.wTree.get_widget("combobox3")
-        self.store = gtk.ListStore(gobject.TYPE_STRING)
-        #populate
-        for element in TYPES:
-            self.store.append([str(element.MIME)])
-        self.comboboxMIME.set_model(self.store)
-        cell = gtk.CellRendererText()
-        self.comboboxMIME.pack_start(cell, True)
-        self.comboboxMIME.add_attribute(cell, 'text',0)
-
-        # Crete treeview widget with file extensions
-        self.treeviewEXT=self.wTree.get_widget("treeview2")
-        self.treemodelEXT=gtk.TreeStore(gobject.TYPE_STRING)
-        self.treeviewEXT.set_model(self.treemodelEXT)
-        # Add treeview header
-        self.treeviewEXT.set_headers_visible(True)
-        renderer=gtk.CellRendererText()
-        column=gtk.TreeViewColumn("Extension:",renderer, text=0)
-        column.set_resizable(True)
-        self.treeviewEXT.append_column(column)
-        renderer=gtk.CellRendererText()
-
-        #populate
-        for element in TYPES:
-            self.insert_row(self.treemodelEXT,None,element.ext)
-        self.treeviewEXT.show()
-
-    def bind_signals(self):
-        """ This method creates a dictionary of pairs
-        signal naem : event, and then binds them
-        using signal_autoconnect method """
-
-        dic = { "on_btnAddFileName_clicked" : \
-                self.buttonAddefaultFileName_clicked,
-                "on_btnRemoveFileName_clicked" : \
-                self.buttonRemoveDefultFileName_clicked,
-                "on_btnAddMIMEExtenesion_clicked" : \
-                self.buttonAddMIMEExtension_clicked,
-                "on_btnAddMIMEType_clicked" : \
-                self.buttonAddMIMEType_clicked,
-                "on_filechooserbutton2_file_set" : \
-                self.filechooserbutton2_file_set,
-                "on_filechooserbutton1_file_set" : \
-                self.filechooserbutton1_file_set,
-                "on_filechooserbutton4_file_set" : \
-                self.filechooserbutton4_file_set,
-                "on_filechooserbutton3_file_set" : \
-                self.filechooserbutton3_file_set,
-                "on_btnRemoveMIMEExtension_clicked" : \
-                self.buttonRemoveMIMEExtension_clicked,
-                "on_btnAddHostName_clicked" : \
-                self.buttonAddHostName_clicked,
-                "on_btnRemoveHostName_clicked" : \
-                self.buttonRemoveHostName_clicked,
-                "on_btnAddHost_clicked" : \
-                self.btnAddHost_clicked,
-                "on_btnAddIP_clicked" : \
-                self.btnAddIP_clicked,
-                "on_btnRemoveHost_clicked" : \
-                self.btnRemoveHost_clicked,
-                "on_btnRemoveIP_clicked" : \
-                self.btnRemoveIP_clicked,
-                "on_save_activate" : \
-                self.serialize_config
-                }
-        # Connect signals
-        self.wTree.signal_autoconnect (dic)
-
-    def insert_row(self, model,parent,
-                   column):
-        """Fuction used to add rows to treeview.
-        @param model  what model tree view uses for storing data
-        @param parent used to nest entries (like dir explorer)
-        @param column the text you want to insert
-        """
-        myiter=model.insert_after(parent,None)
-        model.set_value(myiter,0,column)
-        return myiter
-
-    def delete_rows(self, treeV):
-        """ Function used to remove rows from treeview.
-        @param treeV from which treeview we're deleting
-        """
-        selection = treeV.get_selection()
-        model, selected = selection.get_selected()
-        model.remove(selected)
-
-    def btnAddHost_clicked(self, button):
-        """ Add new host name to treeview """
-        self.insert_row(self.treemodelHost,None, self.ShowDialogBox("Add new 
host name"))
-
-    def btnRemoveHost_clicked(self, button):
-        """ Removes selected file name """
-        self.delete_rows(self.treeviewHost)
-
-    def btnAddIP_clicked(self, button):
-        """ Add new IP address to trreview """
-        self.insert_row(self.treemodelIP,None, self.ShowDialogBox("Add new IP 
address"))
-
-    def btnRemoveIP_clicked(self, button):
-        """ Removes selected file name """
-        self.delete_rows(self.treeviewIP)
-
-    def buttonAddMIMEType_clicked(self, button):
-        """ Add new MIME type to list """
-        self.store.append([self.ShowDialogBox("Add new MIME type")])
-
-    def buttonAddHostName_clicked(self, button):
-        """ Add new Host name to combobox """
-        self.store2.append([self.ShowDialogBox("Add new host")])
-
-    def buttonRemoveHostName_clicked(self, button):
-        """ Removes host name from combobox """
-        self.store2.remove(self.cbHost.get_active_iter())
-
-    def buttonAddefaultFileName_clicked(self, button):
-        """ Adds new entry, with default file name to tree view"""
-        self.insert_row(self.treemodel,None, self.ShowDialogBox("Add new file 
name"))
-
-    def buttonRemoveDefultFileName_clicked(self, button):
-        """ Removes selected file name """
-        self.delete_rows(self.treeview)
-
-    def buttonAddMIMEExtension_clicked(self, button):
-        """ Add new extension """
-        self.insert_row(self.treemodelEXT,None, self.ShowDialogBox("Add new 
extension"))
-
-    def buttonRemoveMIMEExtension_clicked(self, widget):
-        """ Remove selected extension name """
-        self.delete_rows(self.treeviewEXT)
-
-    def filechooserbutton2_file_set(self, widget):
-        """ Place file path in entry field """
-        
self.wTree.get_widget("enManager").set_text(self.wTree.get_widget("filechooserbutton2").get_filename())
-
-    def filechooserbutton4_file_set(self, widget):
-        """ Place file path in entry field """
-        
self.wTree.get_widget("enSystemFolder").set_text(self.wTree.get_widget("filechooserbutton4").get_filename())
-
-    def filechooserbutton3_file_set(self, widget):
-        """ Place file path in entry field """
-        
self.wTree.get_widget("enDocummentRoot").set_text(self.wTree.get_widget("filechooserbutton3").get_filename())
-
-    def filechooserbutton1_file_set(self, widget):
-        """ Place file path in entry field """
-        
self.wTree.get_widget("enStylesheet").set_text(self.wTree.get_widget("filechooserbutton1").get_filename())
-
-    def ShowDialogBox(self, title):
-        """ Shows a typical dilog box with single text box entry field """
-        dia = gtk.Dialog(title)
-        dia.show()
-        entry = gtk.Entry()
-        entry.show()
-        entry.set_activates_default(True)
-        dia.vbox.pack_start(entry)
-        dia.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
-        dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
-        dia.set_default_response(gtk.RESPONSE_OK)
-        response = dia.run()
-        if response == gtk.RESPONSE_OK:
-            name = entry.get_text()
-        dia.destroy()
-        return name
-
-    def run(self):
-        gtk.main()
-
-    # Here begins the functionality implementation
-    def get(self, name):
-        """ Helper/alias function returns widgetwith specified name """
-        return self.wTree.get_widget(name)
-
-    def save_dict_to_xml(self, d, filename="settings.xml"):
-        """ Saves dictionary (with settings) to a XML file"""
-        out = "<?xml version=\"1.0\"?>\n <!--MODIFY THE FILE ONLY IF YOU ARE 
SURE OF WHAT YOU ARE DOING--> \n <MYSERVER> \n"
-        for k, v in d.iteritems():
-            out += "<" + k.__str__() + ">"
-            out += v.__str__()
-            out += "</" + k.__str__() + ">\n"
-        out += "</MYSERVER>"
-
-        FILE = open(filename,"w")
-        FILE.writelines(out)
-        FILE.close()
-
-    def serialize_config(self, widget):
-        """ This function serializes the configurtion settings
-        and creates a dictionary with pairs:
-        name_of_setting: vlue """
-        settings = { 'LANGUGE' : 
self.get("combobox2").get_active_text().split(".")[0].capitalize(), \
-                     'VERBOSITY' :  
self.get("cbVerbosityLevel").get_active_text(), \
-                     'NTHREADS_STATIC' : 
self.get("enAlwysActiveThreads").get_text(), \
-                     'NTHREADS_MAX' : 
self.get("enMaximumNumberOfThreads").get_text(), \
-                     'BUFFER_SIZE' : self.get("enMemoryBuffer").get_text(), \
-                     #TODO Inesrt default filenames from treeview
-                     'CONNECTION_TIMEOUT' : 
self.get("enConnectionTimeOut").get_text(), \
-                     #'USE_ERROR_FILE' : self.get
-                     'MAX_CONNECTIONS' : 
self.get("enMaxConnections").get_text(), \
-                     'MAX_LOG_FILE_SIZE' : 
self.get("enMaxLogFileSize").get_text(), \
-                     'BROWSEFOLDER_CSS' : self.get("enStylesheet").get_text(), 
\
-                     'SERVER_ADMIN' : self.get("entry10").get_text(), \
-                     'GZIP_THRESHOLD' : 
self.get("enGzipCompressionThreshold").get_text(), \
-                     #'FOLLOW_LINKS'
-                     #'MAX_FILESCACHE_SIZE' : self.get("").get_text(), \
-                     #'MAX_FILESCACHE_FILESIZE' : self.get("").get_text(), \
-                     #'MIN_FILESCACHE_FILESIZE' : self.get("").get_text(), \
-                     #'USE_HOME_DIRECTORY' : self.get("").get_text(), \
-                     #'HOME_DIRECTORY' : self.get("").get_text(), \
-                     #'CONTROL_ENABLED' : self.get("").get_text(), \
-                     'ALLOW_CGI' : 'YES' if 
self.get("checkbutton8").get_active() else 'NO', \
-                     'ALLOW_FASTCGI'  : 'YES' if 
self.get("checkbutton12").get_active() else 'NO', \
-                     'ALLOW_ISAPI' : 'YES' if 
self.get("checkbutton9").get_active() else 'NO', \
-                     'ALLOW_WINCGI' : 'YES' if 
self.get("checkbutton11").get_active() else 'NO', \
-                     'ALLOW_MSCGI' : 'YES' if 
self.get("checkbutton10").get_active() else 'NO', \
-                     'ALLOW_SCGI' : 'YES' if 
self.get("checkbutton13").get_active() else 'NO', \
-                     'ALLOW_SEND_LINK' : 'YES' if 
self.get("checkbutton14").get_active() else 'NO', \
-                     'ALLOW_EXTERNAL_COMMANDS' : 'YES' if 
self.get("checkbutton15").get_active() else 'NO', \
-                     'ALLOW_SEND_FILE' : 'YES' if 
self.get("checkbutton16").get_active() else 'NO', \
-                     #'MAX_SERVERS_PROCESSES' : self.get("").get_text(), \
-                     #'SERVERS_PROCESSES_INITIAL_PORT' : 
self.get("").get_text(), \
-                     'CONTROL_ADMIN' : self.get("entry11").get_text(), \
-                     'CONTROL_PASSWORD' : self.get("entry12").get_text(), \
-                      }
-        self.save_dict_to_xml(settings)
-        dia = gtk.Dialog("Settings save")
-        dia.show()
-        dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
-        dia.set_default_response(gtk.RESPONSE_OK)
-        response = dia.run()
-        dia.destroy()
-
-
-
-
-def main():
-    app = ConfigGUIGTK()
-    app.run()
-
-if __name__ == "__main__":
-    main()
-
diff --git a/misc/PyGTK_Control/old/MIMEtypes.py 
b/misc/PyGTK_Control/old/MIMEtypes.py
deleted file mode 100644
index 3081991..0000000
--- a/misc/PyGTK_Control/old/MIMEtypes.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# MyServer
-# Copyright (C) 2002, 2003, 2004, 2007, 2008 The MyServer Team
-# 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/>.
-
-from xml.etree import ElementTree as ET
-
-class MIMEtype:
-    """This type wraps MIME types described in MIMEtypes.xml """
-
-    def __init__(self, ext, MIME, CMD, MANAGER):
-        self.ext = ext
-        self.MIME = MIME
-        self.CMD = CMD
-        self.MANAGER = MANAGER
-    
-    #TODO: Override __str__ method
-        
-  
-def ParseTypes(filename):
-    """ Parse types """
-    sock = open(filename)
-    tree = ET.parse(sock)
-    sock.close()
-    elem = tree.getroot()
-
-    # Create nd populate a list with MIME types
-    MTlist = []
-    for MTypes in elem:
-        ext = MTypes.findtext("EXT")
-        mime = MTypes.findtext("MIME")
-        cmd = MTypes.findtext("CMD")
-        manager = MTypes.findtext("MANAGER")
-        
-        MT = MIMEtype(ext, mime, cmd, elem)
-        
-        MTlist.append(MT)
-   
-    return MTlist
-                
-#To test if it works:  
-if __name__ == "__main__":
-    LIST =  ParseTypes("MIMEtypes.xml")
-    for el in LIST:
-        print "======="
-        print el.ext
-        print el.MIME
-        print el.CMD
-        #TODO: manager node is always None in the input XML - so it prints 
object type
-        print el.MANAGER
-    
diff --git a/misc/PyGTK_Control/old/MIMEtypes.xml 
b/misc/PyGTK_Control/old/MIMEtypes.xml
deleted file mode 100644
index 870e1ca..0000000
--- a/misc/PyGTK_Control/old/MIMEtypes.xml
+++ /dev/null
@@ -1,985 +0,0 @@
-<?xml version="1.0"?>
-
-<MIMETYPES>
-
-<MIMETYPE>
-<EXT>mscgi</EXT>
-<MIME>text/html</MIME>
-<CMD>RUNMSCGI</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>cgi</EXT>
-<MIME>text/html</MIME>
-<CMD>EXECUTE</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>isapi</EXT>
-<MIME>text/html</MIME>
-<CMD>EXECUTEISAPI</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>fcgi</EXT>
-<MIME>text/html</MIME>
-<CMD>EXECUTEFASTCGI</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wincgi</EXT>
-<MIME>text/html</MIME>
-<CMD>EXECUTEWINCGI</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ai</EXT>
-<MIME>application/postscript</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>msxu</EXT>
-<MIME>video/vnd.mpegurl</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ice</EXT>
-<MIME>x-conference/x-cooltalk</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>movie</EXT>
-<MIME>video/x-sgi-movie</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ps</EXT>
-<MIME>application/postscript</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>eps</EXT>
-<MIME>application/postscript</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>smil</EXT>
-<MIME>application/smil</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wmlsc</EXT>
-<MIME>application/vnd.wap.wmlscriptc</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wmlc</EXT>
-<MIME>application/vnd.wap.wmlc</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wbxml</EXT>
-<MIME>application/vnd.wap.wbxml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>smi</EXT>
-<MIME>application/smil</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dll</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>so</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>class</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>exe</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>lzh</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>lha</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dms</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>oda</EXT>
-<MIME>application/oda</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>bin</EXT>
-<MIME>application/octet-stream</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>src</EXT>
-<MIME>application/x-wais-source</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ustar</EXT>
-<MIME>application/x-ustar</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ms</EXT>
-<MIME>application/x-troff-ms</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>me</EXT>
-<MIME>application/x-troff-me</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>man</EXT>
-<MIME>application/x-troff-man</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>roff</EXT>
-<MIME>application/x-troff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tr</EXT>
-<MIME>application/x-troff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>t</EXT>
-<MIME>application/x-troff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>texi</EXT>
-<MIME>application/x-texinfo</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>texinfo</EXT>
-<MIME>application/x-texinfo</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tex</EXT>
-<MIME>application/x-tex</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tcl</EXT>
-<MIME>application/x-tcl</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tar</EXT>
-<MIME>application/x-tar</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sv4crc</EXT>
-<MIME>application/x-sv4crc</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sv4cpio</EXT>
-<MIME>application/x-sv4cpio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sit</EXT>
-<MIME>application/x-stuffit</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>swf</EXT>
-<MIME>application/x-shockwave-flash</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>shar</EXT>
-<MIME>application/x-shar</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sh</EXT>
-<MIME>application/x-sh</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>cpt</EXT>
-<MIME>application/mac-compactpro</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>hqx</EXT>
-<MIME>application/mac-binhex40</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>cdf</EXT>
-<MIME>application/x-netcdf</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ez</EXT>
-<MIME>application/andrew-inset</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>nc</EXT>
-<MIME>application/x-netcdf</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>latex</EXT>
-<MIME>application/x-latex</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>skm</EXT>
-<MIME>application/x-koan</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>skt</EXT>
-<MIME>application/x-koan</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>skd</EXT>
-<MIME>application/x-koan</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>skp</EXT>
-<MIME>application/x-koan</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>js</EXT>
-<MIME>application/x-javascript</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>hdf</EXT>
-<MIME>application/x-hdf</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>gtar</EXT>
-<MIME>application/x-gtar</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>spl</EXT>
-<MIME>application/x-futuresplash</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dvi</EXT>
-<MIME>application/x-dvi</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dxr</EXT>
-<MIME>application/x-director</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dir</EXT>
-<MIME>application/x-director</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>dcr</EXT>
-<MIME>application/x-director</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>csh</EXT>
-<MIME>application/x-csh</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>cpio</EXT>
-<MIME>application/x-cpio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pgn</EXT>
-<MIME>application/x-chess-pgn</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>vcd</EXT>
-<MIME>application/x-cdlink</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>bcpio</EXT>
-<MIME>application/x-bcpio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xsl</EXT>
-<MIME>text/xml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>etx</EXT>
-<MIME>text/x-setext</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tsv</EXT>
-<MIME>text/tab-separated-values</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sgm</EXT>
-<MIME>text/sgml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>sgml</EXT>
-<MIME>text/sgml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>rtf</EXT>
-<MIME>text/rtf</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>rtx</EXT>
-<MIME>text/richtext</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>silo</EXT>
-<MIME>model/mesh</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mesh</EXT>
-<MIME>model/mesh</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>msh</EXT>
-<MIME>model/mesh</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>iges</EXT>
-<MIME>model/iges</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>igs</EXT>
-<MIME>model/iges</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>m3u</EXT>
-<MIME>audio/x-mpegurl</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>avi</EXT>
-<MIME>video/x-msvideo</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mov</EXT>
-<MIME>video/quicktime</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>qt</EXT>
-<MIME>video/quicktime</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mpg</EXT>
-<MIME>video/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mpe</EXT>
-<MIME>video/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mpeg</EXT>
-<MIME>video/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xht</EXT>
-<MIME>application/xhtml+xml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xhtml</EXT>
-<MIME>application/xhtml+xml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xwd</EXT>
-<MIME>image/x-xwindowdump</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xpm</EXT>
-<MIME>image/x-xpixmap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xbm</EXT>
-<MIME>image/x-xbitmap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>rgb</EXT>
-<MIME>image/x-rgb</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ppm</EXT>
-<MIME>image/x-portable-pixmap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pgm</EXT>
-<MIME>image/x-portable-graymap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pbm</EXT>
-<MIME>image/x-portable-bitmap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pnm</EXT>
-<MIME>image/x-portable-anymap</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ras</EXT>
-<MIME>image/x-cmu-raster</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wbmp</EXT>
-<MIME>image/vnd.wap.wbmp</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>djv</EXT>
-<MIME>image/vnd.djvu</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>djvu</EXT>
-<MIME>image/vnd.djvu</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tif</EXT>
-<MIME>image/tiff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>tiff</EXT>
-<MIME>image/tiff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>png</EXT>
-<MIME>image/png</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ief</EXT>
-<MIME>image/ief</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xyz</EXT>
-<MIME>chemical/x-xyz</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pdb</EXT>
-<MIME>chemical/x-pdb</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wav</EXT>
-<MIME>audio/x-wav</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ra</EXT>
-<MIME>audio/x-realaudio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>rpm</EXT>
-<MIME>audio/x-pn-realaudio-plugin</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>rm</EXT>
-<MIME>audio/x-pn-realaudio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ram</EXT>
-<MIME>audio/x-pn-realaudio</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>aifc</EXT>
-<MIME>audio/x-aiff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>aif</EXT>
-<MIME>audio/x-aiff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>aiff</EXT>
-<MIME>audio/x-aiff</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>snd</EXT>
-<MIME>audio/basic</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>au</EXT>
-<MIME>audio/basic</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mp3</EXT>
-<MIME>audio/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mp2</EXT>
-<MIME>audio/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mpga</EXT>
-<MIME>audio/mpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>kar</EXT>
-<MIME>audio/midi</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>midi</EXT>
-<MIME>audio/midi</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>mid</EXT>
-<MIME>audio/midi</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xls</EXT>
-<MIME>application/vnd.ms-excel</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>ppt</EXT>
-<MIME>application/vnd.ms-powerpoint</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>doc</EXT>
-<MIME>application/msword</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>pdf</EXT>
-<MIME>application/pdf</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>zip</EXT>
-<MIME>application/zip</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>css</EXT>
-<MIME>text/css</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>bmp</EXT>
-<MIME>image/bmp</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>vrml</EXT>
-<MIME>model/vrml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>wrl</EXT>
-<MIME>model/vrml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>asc</EXT>
-<MIME>text/html</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>txt</EXT>
-<MIME>text/plain</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>link</EXT>
-<MIME>NONE</MIME>
-<CMD>SENDLINK</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>xml</EXT>
-<MIME>text/xml</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>jpe</EXT>
-<MIME>image/jpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>jpeg</EXT>
-<MIME>image/jpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>jpg</EXT>
-<MIME>image/jpeg</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>gif</EXT>
-<MIME>image/gif</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-<MIMETYPE>
-<EXT>html</EXT>
-<MIME>text/html</MIME>
-<CMD>SEND</CMD>
-<MANAGER>NONE</MANAGER>
-</MIMETYPE>
-
-</MIMETYPES>
diff --git a/misc/PyGTK_Control/old/MyServer Configure.ico 
b/misc/PyGTK_Control/old/MyServer Configure.ico
deleted file mode 100755
index d7a0fa1..0000000
Binary files a/misc/PyGTK_Control/old/MyServer Configure.ico and /dev/null 
differ
diff --git a/misc/PyGTK_Control/old/XMLGui.glade 
b/misc/PyGTK_Control/old/XMLGui.glade
deleted file mode 100644
index ba9ca8c..0000000
--- a/misc/PyGTK_Control/old/XMLGui.glade
+++ /dev/null
@@ -1,1810 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--*- mode: xml -*-->
-<glade-interface>
-  <widget class="GtkWindow" id="ConfigUI">
-    <property name="visible">True</property>
-    <property name="title" translatable="yes">Myserver configuration 
tool</property>
-    <property name="resizable">False</property>
-    <property name="icon">MyServer Configure.ico</property>
-    <child>
-      <widget class="GtkVBox" id="vbox1">
-        <property name="visible">True</property>
-        <child>
-          <widget class="GtkVBox" id="vbox2">
-            <property name="visible">True</property>
-            <child>
-              <widget class="GtkMenuBar" id="btn">
-                <property name="visible">True</property>
-                <child>
-                  <widget class="GtkMenuItem" id="menuitem1">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">_File</property>
-                    <property name="use_underline">True</property>
-                    <child>
-                      <widget class="GtkMenu" id="menuitem1_menu">
-                        <child>
-                          <widget class="GtkImageMenuItem" id="imagemenuitem1">
-                            <property name="visible">True</property>
-                            <property name="label">gtk-open</property>
-                            <property name="use_underline">True</property>
-                            <property name="use_stock">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkImageMenuItem" id="_Revert">
-                            <property name="visible">True</property>
-                            <property name="label">gtk-undo</property>
-                            <property name="use_underline">True</property>
-                            <property name="use_stock">True</property>
-                            <signal name="activate" 
handler="on__Revert_activate"/>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkImageMenuItem" id="run1">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Run</property>
-                            <property name="use_underline">True</property>
-                            <child>
-                              <widget class="GtkMenu" id="run1_menu">
-                                <child>
-                                  <widget class="GtkImageMenuItem" 
id="as_service">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">as Service</property>
-                                    <property 
name="use_underline">True</property>
-                                    <signal name="activate" 
handler="on_as_service_activate"/>
-                                    <child internal-child="image">
-                                      <widget class="GtkImage" id="image29">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="stock">gtk-missing-image</property>
-                                        <property name="icon_size">1</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkImageMenuItem" 
id="as_console">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">as Console</property>
-                                    <property 
name="use_underline">True</property>
-                                    <signal name="activate" 
handler="on_as_console_activate"/>
-                                    <child internal-child="image">
-                                      <widget class="GtkImage" id="image30">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="stock">gtk-bold</property>
-                                        <property name="icon_size">1</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                </child>
-                              </widget>
-                            </child>
-                            <child internal-child="image">
-                              <widget class="GtkImage" id="image28">
-                                <property name="visible">True</property>
-                                <property 
name="stock">gtk-media-play</property>
-                                <property name="icon_size">1</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkImageMenuItem" id="stop1">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Stop</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_stop1_activate"/>
-                            <child>
-                              <widget class="GtkMenu" id="stop1_menu">
-                                <child>
-                                  <widget class="GtkImageMenuItem" 
id="service">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Service</property>
-                                    <property 
name="use_underline">True</property>
-                                    <signal name="activate" 
handler="on_service_activate"/>
-                                    <child internal-child="image">
-                                      <widget class="GtkImage" id="image32">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="stock">gtk-missing-image</property>
-                                        <property name="icon_size">1</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkImageMenuItem" 
id="console">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Console</property>
-                                    <property 
name="use_underline">True</property>
-                                    <signal name="activate" 
handler="on_console_activate"/>
-                                    <child internal-child="image">
-                                      <widget class="GtkImage" id="image33">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="stock">gtk-bold</property>
-                                        <property name="icon_size">1</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                </child>
-                              </widget>
-                            </child>
-                            <child internal-child="image">
-                              <widget class="GtkImage" id="image31">
-                                <property name="visible">True</property>
-                                <property name="stock">gtk-stop</property>
-                                <property name="icon_size">1</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkImageMenuItem" id="save">
-                            <property name="visible">True</property>
-                            <property name="label">gtk-save</property>
-                            <property name="use_underline">True</property>
-                            <property name="use_stock">True</property>
-                            <signal name="activate" 
handler="on_save_activate"/>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkMenuItem" id="menuitem2">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">Ser_vice</property>
-                    <property name="use_underline">True</property>
-                    <child>
-                      <widget class="GtkMenu" id="menuitem2_menu">
-                        <child>
-                          <widget class="GtkMenuItem" id="imagemenuitem6">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Install 
service</property>
-                            <property name="use_underline">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="imagemenuitem7">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Remove 
service</property>
-                            <property name="use_underline">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkMenuItem" id="menuitem3">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">Re_mote</property>
-                    <property name="use_underline">True</property>
-                    <child>
-                      <widget class="GtkMenu" id="menuitem3_menu">
-                        <child>
-                          <widget class="GtkImageMenuItem" id="login">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Login...</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_login_activate"/>
-                            <child internal-child="image">
-                              <widget class="GtkImage" id="image34">
-                                <property name="visible">True</property>
-                                <property 
name="stock">gtk-go-forward</property>
-                                <property name="icon_size">1</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="logout">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Logout</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_logout_activate"/>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="get_config">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Get 
Config</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_get_config_activate"/>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="send_config">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Send 
Config</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_send_config_activate"/>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="connections">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Connections...</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_connections_activate"/>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkMenuItem" id="reboot1">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Reboot..</property>
-                            <property name="use_underline">True</property>
-                            <signal name="activate" 
handler="on_reboot1_activate"/>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkMenuItem" id="menuitem4">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">_Help</property>
-                    <property name="use_underline">True</property>
-                    <child>
-                      <widget class="GtkMenu" id="menuitem4_menu">
-                        <child>
-                          <widget class="GtkImageMenuItem" 
id="imagemenuitem10">
-                            <property name="visible">True</property>
-                            <property name="label">gtk-about</property>
-                            <property name="use_underline">True</property>
-                            <property name="use_stock">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkNotebook" id="notebook1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <child>
-                  <widget class="GtkHPaned" id="hpaned1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <widget class="GtkTable" id="table1">
-                        <property name="visible">True</property>
-                        <property name="n_rows">6</property>
-                        <property name="n_columns">2</property>
-                        <child>
-                          <widget class="GtkLabel" id="label7">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Memory 
buffer (in bytes):</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label8">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Verbosity level:</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label9">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Max log 
file size (in bytes):</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label10">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Always 
active threads:</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label11">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Maximum 
number of threads:</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label12">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Language:</property>
-                            <property 
name="justify">GTK_JUSTIFY_RIGHT</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">5</property>
-                            <property name="bottom_attach">6</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enMemoryBuffer">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">102400</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enMaxLogFileSize">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">1048576</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enAlwysActiveThreads">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">5</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" 
id="enMaximumNumberOfThreads">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">200</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBox" id="cbVerbosityLevel">
-                            <property name="visible">True</property>
-                            <property name="items" translatable="yes">None
-Normal
-Higher
-Highest</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBox" id="combobox2">
-                            <property name="visible">True</property>
-                            <property name="items" 
translatable="yes">german.xml
-itliano.xml
-russian_koi8r.xml
-brazilian.xml
-french.xml
-russian.xml
-english.xml
-romnaa.xml
-spnish.xml
-
-</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">5</property>
-                            <property name="bottom_attach">6</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="resize">True</property>
-                        <property name="shrink">True</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label1">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">System</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox3">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkTable" id="table2">
-                        <property name="visible">True</property>
-                        <property name="n_rows">5</property>
-                        <property name="n_columns">3</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <widget class="GtkFileChooserButton" 
id="filechooserbutton1">
-                            <property name="visible">True</property>
-                            <property name="title" 
translatable="yes">Browse..</property>
-                            <property name="width_chars">7</property>
-                            <signal name="file_set" 
handler="on_filechooserbutton1_file_set"/>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">2</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="checkbutton1">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" 
translatable="yes">Personalized error pages:</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enStylesheet">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">/sys/css/browsestyle.css</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" 
id="enGzipCompressionThreshold">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">1048576</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enMaxConnections">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">0</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enConnectionTimeOut">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">60</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label16">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Stylesheet</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label15">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Gzip 
compression threshold (in bytes):</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label14">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Max 
connections:</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label13">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Connection time-out (in sec):</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkVBox" id="vbox4">
-                        <property name="visible">True</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox1">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkTreeView" id="treeview1">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property 
name="extension_events">GDK_EXTENSION_EVENTS_CURSOR</property>
-                                <property 
name="headers_visible">False</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkVBox" id="vbox5">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkButton" 
id="btnAddFileName">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="label">gtk-add</property>
-                                    <property name="use_stock">True</property>
-                                    <property name="response_id">0</property>
-                                    <signal name="clicked" 
handler="on_btnAddFileName_clicked"/>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkButton" 
id="btnRemoveFileName">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property 
name="label">gtk-remove</property>
-                                    <property name="use_stock">True</property>
-                                    <property name="response_id">0</property>
-                                    <signal name="clicked" 
handler="on_btnRemoveFileName_clicked"/>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label2">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Server</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="position">1</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox2">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkVBox" id="vbox6">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkScrolledWindow" 
id="scrolledwindow1">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property 
name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <property 
name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <child>
-                              <widget class="GtkTreeView" id="treeview2">
-                                <property name="height_request">300</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property 
name="enable_grid_lines">GTK_TREE_VIEW_GRID_LINES_HORIZONTAL</property>
-                              </widget>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="btnAddMIMEExtenesion">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label">gtk-add</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                            <signal name="clicked" 
handler="on_btnAddMIMEExtenesion_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" 
id="btnRemoveMIMEExtension">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label">gtk-remove</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                            <signal name="clicked" 
handler="on_btnRemoveMIMEExtension_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkTable" id="table3">
-                        <property name="visible">True</property>
-                        <property name="n_rows">5</property>
-                        <property name="n_columns">2</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="btnAddMIMEType">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label">gtk-add</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                            <signal name="clicked" 
handler="on_btnAddMIMEType_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options"></property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkFileChooserButton" 
id="filechooserbutton2">
-                            <property name="visible">True</property>
-                            <property name="title" 
translatable="yes">Browse..</property>
-                            <property name="width_chars">7</property>
-                            <signal name="file_set" 
handler="on_filechooserbutton2_file_set"/>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                            <property name="x_options"></property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label19">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">MIME 
Type:</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBox" id="combobox3">
-                            <property name="visible">True</property>
-                            <property name="active">0</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label20">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Action:</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBox" id="cbAction">
-                            <property name="visible">True</property>
-                            <property name="row_span_column">1</property>
-                            <property name="active">0</property>
-                            <property name="items" translatable="yes">Send the 
file
-URL link
-Run as CGI
-Run as FastCGI
-Run as SCGI
-Run as ISAPI
-Run as MSCGI
-Run as WINCGI
-Execute
-Execute as an ISAPI module
-Execute self contined FastCGI
-Run self contained SCGI</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label21">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Manager:</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="enManager">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="text" 
translatable="yes">NONE</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label3">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">MIME</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="position">2</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox7">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkHBox" id="hbox4">
-                        <property name="height_request">30</property>
-                        <property name="visible">True</property>
-                        <property name="homogeneous">True</property>
-                        <child>
-                          <widget class="GtkVBox" id="vbox13">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="label23">
-                                <property name="width_request">100</property>
-                                <property name="height_request">20</property>
-                                <property name="visible">True</property>
-                                <property name="xpad">1</property>
-                                <property name="ypad">5</property>
-                                <property name="label" 
translatable="yes">Name:</property>
-                                <property 
name="single_line_mode">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox11">
-                            <property name="visible">True</property>
-                            <property name="spacing">3</property>
-                            <property name="homogeneous">True</property>
-                            <child>
-                              <widget class="GtkComboBox" id="combobox5">
-                                <property name="height_request">20</property>
-                                <property name="visible">True</property>
-                                <property name="has_frame">False</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="padding">3</property>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox14">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkButton" id="btnAddHostName">
-                                <property name="height_request">20</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="label">gtk-add</property>
-                                <property name="use_stock">True</property>
-                                <property name="response_id">0</property>
-                                <signal name="clicked" 
handler="on_btnAddHostName_clicked"/>
-                              </widget>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox10">
-                            <property name="visible">True</property>
-                            <property name="homogeneous">True</property>
-                            <child>
-                              <widget class="GtkButton" id="btnRemoveHostName">
-                                <property name="height_request">20</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="label">gtk-remove</property>
-                                <property name="use_stock">True</property>
-                                <property name="response_id">0</property>
-                                <signal name="clicked" 
handler="on_btnRemoveHostName_clicked"/>
-                              </widget>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">4</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkHSeparator" id="hseparator1">
-                        <property name="visible">True</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkNotebook" id="notebook2">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <child>
-                          <widget class="GtkVBox" id="vbox22">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkHBox" id="hbox14">
-                                <property name="visible">True</property>
-                                <property name="homogeneous">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label36">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Protocol:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkComboBox" id="ddProtocol">
-                                    <property name="visible">True</property>
-                                    <property name="items">HTTP
-FTP
-</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox15">
-                                <property name="visible">True</property>
-                                <property name="homogeneous">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label37">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">SSL Private Key:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkFileChooserButton" 
id="filechooserbutton5">
-                                    <property name="visible">True</property>
-                                    <property 
name="preview_widget_active">False</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox16">
-                                <property name="visible">True</property>
-                                <property name="homogeneous">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label38">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">SSL Certyfikate:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkFileChooserButton" 
id="filechooserbutton6">
-                                    <property name="visible">True</property>
-                                    <property 
name="preview_widget_active">False</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox17">
-                                <property name="visible">True</property>
-                                <property name="homogeneous">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label39">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">SSL Password:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkEntry" id="enPassword">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">3</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label24">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Protocol</property>
-                          </widget>
-                          <packing>
-                            <property name="type">tab</property>
-                            <property name="tab_fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox3">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkVBox" id="vbox12">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkTreeView" 
id="treeviewHost">
-                                    <property name="visible">True</property>
-                                    <property 
name="app_paintable">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property 
name="headers_clickable">True</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkHSeparator" 
id="hseparator2">
-                                    <property name="visible">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkHBox" id="hbox8">
-                                    <property name="visible">True</property>
-                                    <property 
name="homogeneous">True</property>
-                                    <child>
-                                      <widget class="GtkButton" 
id="btnAddHost">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property 
name="receives_default">True</property>
-                                        <property name="label" 
translatable="yes">Add..</property>
-                                        <property 
name="response_id">0</property>
-                                        <signal name="clicked" 
handler="on_btnAddHost_clicked"/>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkButton" 
id="btnRemoveHost">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property 
name="receives_default">True</property>
-                                        <property name="label" 
translatable="yes">Remove</property>
-                                        <property 
name="response_id">0</property>
-                                        <signal name="clicked" 
handler="on_btnRemoveHost_clicked"/>
-                                      </widget>
-                                      <packing>
-                                        <property 
name="pack_type">GTK_PACK_END</property>
-                                        <property name="position">1</property>
-                                      </packing>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkVBox" id="vbox15">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkTreeView" id="treeviewIP">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property 
name="headers_clickable">True</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkHSeparator" 
id="hseparator3">
-                                    <property name="visible">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkHBox" id="hbox9">
-                                    <property name="visible">True</property>
-                                    <property 
name="homogeneous">True</property>
-                                    <child>
-                                      <widget class="GtkButton" id="btnAddIP">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property 
name="receives_default">True</property>
-                                        <property name="label" 
translatable="yes">Add..</property>
-                                        <property 
name="response_id">0</property>
-                                        <signal name="clicked" 
handler="on_btnAddIP_clicked"/>
-                                      </widget>
-                                      <packing>
-                                        <property 
name="expand">False</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkButton" 
id="btnRemoveIP">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property 
name="receives_default">True</property>
-                                        <property name="label" 
translatable="yes">Remove</property>
-                                        <property 
name="response_id">0</property>
-                                        <signal name="clicked" 
handler="on_btnRemoveIP_clicked"/>
-                                      </widget>
-                                      <packing>
-                                        <property 
name="expand">False</property>
-                                        <property name="position">1</property>
-                                      </packing>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox10">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkVBox" id="vbox16">
-                                    <property name="visible">True</property>
-                                    <child>
-                                      <widget class="GtkLabel" id="label34">
-                                        <property 
name="visible">True</property>
-                                        <property name="label" 
translatable="yes">Port:</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <placeholder/>
-                                    </child>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkVBox" id="vbox17">
-                                    <property name="visible">True</property>
-                                    <child>
-                                      <widget class="GtkEntry" 
id="enPortNumber">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property 
name="max_length">5</property>
-                                        <property name="text" 
translatable="yes">8080</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <placeholder/>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label25">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Arpa</property>
-                          </widget>
-                          <packing>
-                            <property name="type">tab</property>
-                            <property name="position">1</property>
-                            <property name="tab_fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox18">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="label18">
-                                <property name="visible">True</property>
-                                <property name="label" 
translatable="yes">Enable:</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox11">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkVBox" id="vbox19">
-                                    <property name="visible">True</property>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton8">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">CGI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton9">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">ISAPI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">1</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton10">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">MSCGI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">2</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton11">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">WINCGI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">3</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton12">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">FASTCGI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">4</property>
-                                      </packing>
-                                    </child>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkVBox" id="vbox20">
-                                    <property name="visible">True</property>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton13">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">SCGI</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton14">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">SEND LINK</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">1</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton15">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">EXTERNAL COMMANDS</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">2</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkCheckButton" 
id="checkbutton16">
-                                        <property 
name="visible">True</property>
-                                        <property 
name="can_focus">True</property>
-                                        <property name="label" 
translatable="yes">SEND FILE</property>
-                                        <property 
name="response_id">0</property>
-                                        <property 
name="draw_indicator">True</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">3</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <placeholder/>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label26">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">HTTP 
Services</property>
-                          </widget>
-                          <packing>
-                            <property name="type">tab</property>
-                            <property name="position">2</property>
-                            <property name="tab_fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox21">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkHBox" id="hbox12">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label22">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Documment root:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                                <child>
-                                  <widget class="GtkFileChooserButton" 
id="filechooserbutton3">
-                                    <property name="visible">True</property>
-                                    <property 
name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox13">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label35">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">System folder:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                                <child>
-                                  <widget class="GtkFileChooserButton" 
id="filechooserbutton4">
-                                    <property name="visible">True</property>
-                                    <property 
name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label27">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Directories</property>
-                          </widget>
-                          <packing>
-                            <property name="type">tab</property>
-                            <property name="position">3</property>
-                            <property name="tab_fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox9">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkHBox" id="hbox6">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label32">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Accesses log file:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkEntry" id="entry13">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox7">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label33">
-                                    <property name="visible">True</property>
-                                    <property name="label" 
translatable="yes">Wrning log file:</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkEntry" id="entry14">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">4</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label28">
-                            <property name="visible">True</property>
-                            <property name="label" 
translatable="yes">Logs</property>
-                          </widget>
-                          <packing>
-                            <property name="type">tab</property>
-                            <property name="position">4</property>
-                            <property name="tab_fill">False</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label4">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Hosts</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="position">3</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkTable" id="table4">
-                    <property name="visible">True</property>
-                    <property name="n_rows">4</property>
-                    <property name="n_columns">2</property>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label29">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Administrtor 
e-mail:</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label30">
-                        <property name="visible">True</property>
-                        <property name="label" 
translatable="yes">Administrator user name:</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label31">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Administrtor 
password:</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckButton" id="checkbutton2">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="label" translatable="yes">Enable 
control protocol</property>
-                        <property name="use_underline">True</property>
-                        <property name="response_id">0</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">3</property>
-                        <property name="bottom_attach">4</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="entry10">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="text" 
translatable="yes">address@hidden</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="entry11">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="entry12">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label5">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">Administrator</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="position">4</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox5">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkVBox" id="vbox8">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkLabel" id="label17">
-                            <property name="visible">True</property>
-                            <property 
name="yalign">0.57999998331069946</property>
-                            <property name="ypad">1</property>
-                            <property name="label" 
translatable="yes">Enable:</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="checkbutton3">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" 
translatable="yes">Anonymous Access</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="checkbutton4">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" 
translatable="yes">Asynchronous Commands</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="checkbutton5">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" 
translatable="yes">Pipelining</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="checkbutton6">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" translatable="yes">Write 
Commands</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="position">4</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckButton" id="checkbutton7">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="label" translatable="yes">Anonymous 
Needs Password</property>
-                        <property name="use_underline">True</property>
-                        <property name="response_id">0</property>
-                        <property name="active">True</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">5</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label6">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">FTP 
Services</property>
-                  </widget>
-                  <packing>
-                    <property name="type">tab</property>
-                    <property name="position">5</property>
-                    <property name="tab_fill">False</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkMenu" id="menu1">
-    <property name="visible">True</property>
-  </widget>
-</glade-interface>



commit 9b1a0c54d95307660a0feafb0d2354a0d2c51f4a
Merge: e95ef51 b1b88f0
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 23:28:19 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit abdcc4fc662dd4923cf11733e6401bf142b348b4
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 23:26:19 2009 +0200

    Open handles multiple levels of definitions.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index a262636..c7f5f9b 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -321,18 +321,20 @@ class PyGTKControl():
                 row[3] = ''
                 row[4] = False
                 row[5] = {}
+                child = model.iter_children(i)
+                if child is not None: # remove node children
+                    while model.remove(child):
+                        pass
                 i = model.iter_next(i)
 
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
+        
         def put_to_unknown(definition):
             print('Unknown option:', definition, sep = '\n')
-        self.on_new_menu_item_activate()
-        for definition in config.get_definitions():
+            
+        def get_value_and_attributes(definition):
             name = definition.get_name()
-            if name not in self.options:
-                put_to_unknown(definition)
-                continue
             enabled = True
             try:
                 value = definition.get_attribute('value')
@@ -342,6 +344,27 @@ class PyGTKControl():
                 value_check = False
             attributes = definition.get_attributes()
             attributes.pop('value', None)
+            return (name, enabled, value, value_check, attributes, )
+
+        def add_children(parent, definition, model):
+            if isinstance(definition, DefinitionTree):
+                for d in definition.get_definitions():
+                    name, enabled, value, value_check, attributes = \
+                        get_value_and_attributes(d)
+                    i = model.append(
+                        parent,
+                        (name, '', enabled, value, value_check, attributes, ))
+                    add_children(i, d, model)
+        
+        self.on_new_menu_item_activate()
+        for definition in config.get_definitions():
+            name, enabled, value, value_check, attributes = \
+                get_value_and_attributes(definition)
+            
+            if name not in self.options:
+                put_to_unknown(definition)
+                continue
+            
             tree = self.tabs[self.options[name]][1]
             model = tree.get_model()
             i = model.iter_children(None)
@@ -352,6 +375,7 @@ class PyGTKControl():
             row[3] = value
             row[4] = value_check
             row[5] = attributes
+            add_children(i, definition, model)
 
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''



commit b816fd85420cd9f5ba87c0a5508f3c2b9c22edbd
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 22:58:53 2009 +0200

    One level open file working.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 0750cc9..a262636 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -342,13 +342,23 @@ class PyGTKControl():
                 value_check = False
             attributes = definition.get_attributes()
             attributes.pop('value', None)
-            self.options[name] = (enabled, value, value_check, attributes, )
+            tree = self.tabs[self.options[name]][1]
+            model = tree.get_model()
+            i = model.iter_children(None)
+            while model[i][0] != name:
+                i = model.iter_next(i)
+            row = model[i]
+            row[2] = enabled
+            row[3] = value
+            row[4] = value_check
+            row[5] = attributes
 
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''
 
         # segregate options by tab
         segregated_options = {} # tab name => option names
+        self.options = {} # option name => tab name
         for option in GUIConfig.options:
             tab = 'other'
             for prefix in GUIConfig.tabs:
@@ -358,6 +368,7 @@ class PyGTKControl():
             if not segregated_options.has_key(tab):
                 segregated_options[tab] = []
             segregated_options[tab].append(option)
+            self.options[option] = tab
 
         self.tabs = {} # tab name => (table, tree, )
         for tab in GUIConfig.tabs + ['other', 'unknown']:



commit 816ba0b0fd7a695ba9d91ded059b771aa3868d48
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 22:49:57 2009 +0200

    Store options state in additional tree columns.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 1dd28df..0750cc9 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -62,11 +62,10 @@ class Connection():
         return self.widgets.get_widget('password_entry').get_text()
 
 class EditionTable(gtk.Table):
-    def __init__(self, tree, options):
+    def __init__(self, tree):
         gtk.Table.__init__(self, 8, 3)
         
         tree.connect('cursor-changed', self.cursor_changed)
-        self.options = options
         self.last_selected = None
 
         enabled_label = gtk.Label('enabled:')
@@ -155,11 +154,11 @@ class EditionTable(gtk.Table):
                 attributes[self.attributes_field.get_value(i, 0)] = \
                     self.attributes_field.get_value(i, 1)
                 i = self.attributes_field.iter_next(i)
-            self.options[self.last_selected] = (
-                self.enabled_field.get_active(),
-                self.value_field.get_text(),
-                self.value_check_field.get_active(),
-                attributes, )
+            row = tree.get_model()[self.last_selected]
+            row[2] = self.enabled_field.get_active()
+            row[3] = self.value_field.get_text()
+            row[4] = self.value_check_field.get_active()
+            row[5] = attributes
             
     def cursor_changed(self, tree):
         self.save_changed(tree)
@@ -167,16 +166,16 @@ class EditionTable(gtk.Table):
         self.clear()
         
         self.last_selected = current = self.get_selected(tree)
-        enabled, value, value_check, attributes = self.options[current]
-        self.enabled_field.set_active(enabled)
-        self.value_field.set_text(value)
-        self.value_check_field.set_active(value_check)
-        for attribute in attributes:
-            self.attributes_field.append((attribute, attributes[attribute], ))
+        row = tree.get_model()[current]
+        self.enabled_field.set_active(row[2])
+        self.value_field.set_text(row[3])
+        self.value_check_field.set_active(row[4])
+        for attribute in row[5]:
+            self.attributes_field.append((attribute, row[5][attribute], ))
 
     def get_selected(self, tree):
         model, selected = tree.get_selection().get_selected()
-        return model[selected][0]
+        return selected
     
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -310,12 +309,19 @@ class PyGTKControl():
         '''Clears configuration.'''
         if widget is not None:
             self.path = None
-        for option in self.options:
-            self.options[option] = (False, '', True, {}, )
         for tab in self.tabs:
             table, tree = self.tabs[tab]
+            model = tree.get_model()
             table.clear()
             tree.get_selection().unselect_all()
+            i = model.iter_children(None)
+            while i is not None: # iterate over options
+                row = model[i]
+                row[2] = False
+                row[3] = ''
+                row[4] = False
+                row[5] = {}
+                i = model.iter_next(i)
 
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
@@ -342,7 +348,7 @@ class PyGTKControl():
         '''Reads known options from file and prepares GUI.'''
 
         # segregate options by tab
-        segregated_options = {}
+        segregated_options = {} # tab name => option names
         for option in GUIConfig.options:
             tab = 'other'
             for prefix in GUIConfig.tabs:
@@ -353,14 +359,18 @@ class PyGTKControl():
                 segregated_options[tab] = []
             segregated_options[tab].append(option)
 
-        self.tabs = {} # tab => definitions
-        # option name => (enabled, value, value_check, attributes, )
-        self.options = {}
+        self.tabs = {} # tab name => (table, tree, )
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             options = segregated_options.get(tab, [])
             panels = gtk.HBox()
             
-            tree = gtk.TreeView(gtk.TreeStore(gobject.TYPE_STRING))
+            tree = gtk.TreeView(gtk.TreeStore(
+                    gobject.TYPE_STRING, # option name
+                    gobject.TYPE_STRING, # option tooltip
+                    gobject.TYPE_BOOLEAN, # enabled
+                    gobject.TYPE_STRING, # value
+                    gobject.TYPE_BOOLEAN, # value_check
+                    gobject.TYPE_PYOBJECT)) # attributes dict
             tree_model = tree.get_model()
             tree.set_headers_visible(False)
             tree_column = gtk.TreeViewColumn()
@@ -375,18 +385,17 @@ class PyGTKControl():
             tree_scroll.set_border_width(5)
             tree_scroll.add(tree)
             panels.pack_start(tree_scroll)
-            table = EditionTable(tree, self.options)
+            table = EditionTable(tree)
             panels.pack_start(table)
 
             self.tabs[tab] = (table, tree, )
             
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
-                tooltip = gtk.Tooltip()
-                tooltip.set_text(tooltip_text)
-                it = tree_model.append(None, (option, ))
-                self.options[option] = None # anything, will be set up later
-                tree.set_tooltip_row(tooltip, tree_model[it].path) # not 
working
+                # all but first two columns will be set to defaults later by
+                # on_new_menu_item_activate
+                tree_model.append(None, (option, tooltip_text, False, '',
+                                         False, {}, ))
              
             self.widgets.get_widget('notebook').append_page(
                 panels, gtk.Label(tab))



commit 814c6f4aa71fcfc27ffa488e42999d90b8940cde
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 17:21:13 2009 +0200

    Fixed opening flat files (no trees for now).

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 073fc27..1dd28df 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -320,7 +320,6 @@ class PyGTKControl():
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
         def put_to_unknown(definition):
-            self.unknown.append(definition)
             print('Unknown option:', definition, sep = '\n')
         self.on_new_menu_item_activate()
         for definition in config.get_definitions():
@@ -328,24 +327,16 @@ class PyGTKControl():
             if name not in self.options:
                 put_to_unknown(definition)
                 continue
-            check, field, var = self.options[name]
-            if (isinstance(definition, DefinitionElement) and var == 'list') 
or \
-                    (isinstance(definition, DefinitionTree) and var != 'list'):
-                put_to_unknown(definition)
-                continue
-            self.definitions[name] = definition
-            check.set_active(True)
-            if var == 'string':
-                field.set_text(definition.get_attribute('value'))
-            elif var == 'integer':
-                field.set_value(int(definition.get_attribute('value')))
-            elif var == 'bool':
-                if definition.get_attribute('value').upper() != 'YES':
-                    field.set_active(1)
-            elif var == 'list':
-                for definition in definition.get_definitions():
-                    field.get_model().append(
-                        (definition.get_attribute('value'), ))
+            enabled = True
+            try:
+                value = definition.get_attribute('value')
+                value_check = True
+            except KeyError:
+                value = ''
+                value_check = False
+            attributes = definition.get_attributes()
+            attributes.pop('value', None)
+            self.options[name] = (enabled, value, value_check, attributes, )
 
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''



commit 15879dec8ca6c87ac9d8e60b4b8ccb87b2fd53d4
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 17:18:40 2009 +0200

    Added get_attributes to Definition.

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index ebd62d5..af5dcf0 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -42,6 +42,10 @@ class Definition():
         '''Set definition name, None means no name.'''
         self.name = name
 
+    def get_attributes(self):
+        '''Get dict of all attributes.'''
+        return self.attributes
+
     def get_attribute(self, key):
         '''Get value of attribute key.'''
         return self.attributes[key]
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
index b7917cf..9cb5bcc 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
@@ -40,6 +40,7 @@ class DefinitionTest(unittest.TestCase):
         definition = Definition('with attributes', {'a': 'b', 'c': 'd'})
         self.assertEqual('b', definition.get_attribute('a'))
         self.assertEqual('d', definition.get_attribute('c'))
+        self.assertEqual({'a': 'b', 'c': 'd'}, definition.get_attributes())
         self.assertRaises(KeyError, definition.get_attribute, 'e')
         definition.set_attribute('e', 'f')
         self.assertEqual('f', definition.get_attribute('e'))



commit 44d0809025b967fbed0b62113b02b069921ed4d5
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 17:04:41 2009 +0200

    Access definitions by name, not by TreeIter.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 47461b2..073fc27 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -62,11 +62,11 @@ class Connection():
         return self.widgets.get_widget('password_entry').get_text()
 
 class EditionTable(gtk.Table):
-    def __init__(self, tree, definitions):
+    def __init__(self, tree, options):
         gtk.Table.__init__(self, 8, 3)
         
         tree.connect('cursor-changed', self.cursor_changed)
-        self.definitions = definitions
+        self.options = options
         self.last_selected = None
 
         enabled_label = gtk.Label('enabled:')
@@ -151,11 +151,11 @@ class EditionTable(gtk.Table):
         if self.last_selected is not None:
             attributes = {}
             i = self.attributes_field.iter_children(None)
-            while i is not None:
+            while i is not None: # iterate over attributes
                 attributes[self.attributes_field.get_value(i, 0)] = \
                     self.attributes_field.get_value(i, 1)
                 i = self.attributes_field.iter_next(i)
-            self.definitions[self.last_selected] = (
+            self.options[self.last_selected] = (
                 self.enabled_field.get_active(),
                 self.value_field.get_text(),
                 self.value_check_field.get_active(),
@@ -167,7 +167,7 @@ class EditionTable(gtk.Table):
         self.clear()
         
         self.last_selected = current = self.get_selected(tree)
-        enabled, value, value_check, attributes = self.definitions[current]
+        enabled, value, value_check, attributes = self.options[current]
         self.enabled_field.set_active(enabled)
         self.value_field.set_text(value)
         self.value_check_field.set_active(value_check)
@@ -176,12 +176,7 @@ class EditionTable(gtk.Table):
 
     def get_selected(self, tree):
         model, selected = tree.get_selection().get_selected()
-        current = None
-        for it in self.definitions:
-            if model[it].path == model[selected].path:
-                current = it
-                break
-        return current
+        return model[selected][0]
     
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
@@ -315,10 +310,10 @@ class PyGTKControl():
         '''Clears configuration.'''
         if widget is not None:
             self.path = None
+        for option in self.options:
+            self.options[option] = (False, '', True, {}, )
         for tab in self.tabs:
-            definitions, table, tree = self.tabs[tab]
-            for it in definitions:
-                definitions[it] = (False, '', True, {}, )
+            table, tree = self.tabs[tab]
             table.clear()
             tree.get_selection().unselect_all()
 
@@ -368,9 +363,9 @@ class PyGTKControl():
             segregated_options[tab].append(option)
 
         self.tabs = {} # tab => definitions
+        # option name => (enabled, value, value_check, attributes, )
+        self.options = {}
         for tab in GUIConfig.tabs + ['other', 'unknown']:
-            # TreeIterator => (enabled, value, value_check, attributes, )
-            definitions = {}
             options = segregated_options.get(tab, [])
             panels = gtk.HBox()
             
@@ -389,17 +384,17 @@ class PyGTKControl():
             tree_scroll.set_border_width(5)
             tree_scroll.add(tree)
             panels.pack_start(tree_scroll)
-            table = EditionTable(tree, definitions)
+            table = EditionTable(tree, self.options)
             panels.pack_start(table)
 
-            self.tabs[tab] = (definitions, table, tree, )
+            self.tabs[tab] = (table, tree, )
             
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
                 tooltip = gtk.Tooltip()
                 tooltip.set_text(tooltip_text)
                 it = tree_model.append(None, (option, ))
-                definitions[it] = (False, '', True, {})
+                self.options[option] = None # anything, will be set up later
                 tree.set_tooltip_row(tooltip, tree_model[it].path) # not 
working
              
             self.widgets.get_widget('notebook').append_page(



commit dc0f5b23bd5df3d4fbd81bc797042053c97eeb25
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 16:48:09 2009 +0200

    Removed name field, added enabled field.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 9b207c5..47461b2 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -69,10 +69,11 @@ class EditionTable(gtk.Table):
         self.definitions = definitions
         self.last_selected = None
 
-        name_label = gtk.Label('name:')
-        self.name_field = name_entry = gtk.Entry()
-        self.attach(name_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
-        self.attach(name_entry, 1, 3, 0, 1, yoptions = gtk.FILL)
+        enabled_label = gtk.Label('enabled:')
+        self.enabled_field = enabled_checkbutton = gtk.CheckButton()
+        enabled_checkbutton.set_tooltip_text('If not active, definition won\'t 
be included in saved configuration.')
+        self.attach(enabled_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(enabled_checkbutton, 1, 3, 0, 1, yoptions = gtk.FILL)
 
         value_label = gtk.Label('value:')
         self.value_field = value_entry = gtk.Entry()
@@ -140,7 +141,7 @@ class EditionTable(gtk.Table):
         self.attach(attributes_scroll, 0, 3, 7, 8)
 
     def clear(self):
-        self.name_field.set_text('')
+        self.enabled_field.set_active(False)
         self.value_field.set_text('')
         self.value_check_field.set_active(False)
         self.attributes_field.clear()
@@ -155,6 +156,7 @@ class EditionTable(gtk.Table):
                     self.attributes_field.get_value(i, 1)
                 i = self.attributes_field.iter_next(i)
             self.definitions[self.last_selected] = (
+                self.enabled_field.get_active(),
                 self.value_field.get_text(),
                 self.value_check_field.get_active(),
                 attributes, )
@@ -165,7 +167,8 @@ class EditionTable(gtk.Table):
         self.clear()
         
         self.last_selected = current = self.get_selected(tree)
-        value, value_check, attributes = self.definitions[current]
+        enabled, value, value_check, attributes = self.definitions[current]
+        self.enabled_field.set_active(enabled)
         self.value_field.set_text(value)
         self.value_check_field.set_active(value_check)
         for attribute in attributes:
@@ -315,7 +318,7 @@ class PyGTKControl():
         for tab in self.tabs:
             definitions, table, tree = self.tabs[tab]
             for it in definitions:
-                definitions[it] = ('', True, {}, )
+                definitions[it] = (False, '', True, {}, )
             table.clear()
             tree.get_selection().unselect_all()
 
@@ -366,7 +369,8 @@ class PyGTKControl():
 
         self.tabs = {} # tab => definitions
         for tab in GUIConfig.tabs + ['other', 'unknown']:
-            definitions = {} # TreeIterator => (value, value_check, 
attributes, )
+            # TreeIterator => (enabled, value, value_check, attributes, )
+            definitions = {}
             options = segregated_options.get(tab, [])
             panels = gtk.HBox()
             
@@ -395,7 +399,7 @@ class PyGTKControl():
                 tooltip = gtk.Tooltip()
                 tooltip.set_text(tooltip_text)
                 it = tree_model.append(None, (option, ))
-                definitions[it] = ('', True, {})
+                definitions[it] = (False, '', True, {})
                 tree.set_tooltip_row(tooltip, tree_model[it].path) # not 
working
              
             self.widgets.get_widget('notebook').append_page(



commit 185d91663d56e922f1c789bb8929cb6021bbe264
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 20 13:09:39 2009 +0200

    Working new file.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 0b3c292..9b207c5 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -144,6 +144,7 @@ class EditionTable(gtk.Table):
         self.value_field.set_text('')
         self.value_check_field.set_active(False)
         self.attributes_field.clear()
+        self.last_selected = None
 
     def save_changed(self, tree):
         if self.last_selected is not None:
@@ -311,23 +312,12 @@ class PyGTKControl():
         '''Clears configuration.'''
         if widget is not None:
             self.path = None
-        self.definitions = {} # option name => corresponding Definition
-        self.unknown = [] # list of unknown definitions
-        for option in self.options:
-            check, field, var = self.options.get(option)
-            if var != 'list':
-                self.definitions[option] = DefinitionElement(option)
-            else:
-                self.definitions[option] = DefinitionTree(option)
-            check.set_active(False)
-            if var == 'string':
-                field.set_text('')
-            elif var == 'integer':
-                field.set_value(0)
-            elif var == 'bool':
-                field.set_active(0)
-            elif var == 'list':
-                field.get_model().clear()
+        for tab in self.tabs:
+            definitions, table, tree = self.tabs[tab]
+            for it in definitions:
+                definitions[it] = ('', True, {}, )
+            table.clear()
+            tree.get_selection().unselect_all()
 
     def set_up(self, config):
         '''Reads configuration from given config instance.'''
@@ -374,10 +364,9 @@ class PyGTKControl():
                 segregated_options[tab] = []
             segregated_options[tab].append(option)
 
-        tabs = {} # tab => definitions
+        self.tabs = {} # tab => definitions
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             definitions = {} # TreeIterator => (value, value_check, 
attributes, )
-            tabs[tab] = definitions
             options = segregated_options.get(tab, [])
             panels = gtk.HBox()
             
@@ -396,20 +385,23 @@ class PyGTKControl():
             tree_scroll.set_border_width(5)
             tree_scroll.add(tree)
             panels.pack_start(tree_scroll)
-            panels.pack_start(EditionTable(tree, definitions))
+            table = EditionTable(tree, definitions)
+            panels.pack_start(table)
+
+            self.tabs[tab] = (definitions, table, tree, )
             
             for option in options:
                 tooltip_text, var = GUIConfig.options[option]
                 tooltip = gtk.Tooltip()
                 tooltip.set_text(tooltip_text)
                 it = tree_model.append(None, (option, ))
-                definitions[it] = ('', False, {})
+                definitions[it] = ('', True, {})
                 tree.set_tooltip_row(tooltip, tree_model[it].path) # not 
working
              
             self.widgets.get_widget('notebook').append_page(
                 panels, gtk.Label(tab))
 
-        # self.on_new_menu_item_activate()
+        self.on_new_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 
 PyGTKControl()



commit 33d2bdd4bb69fb4584643a2af593089461b7993a
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 19 23:51:14 2009 +0200

    TreeViews are put inside ScrolledWindows to get scrollbars.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 2094b9d..0b3c292 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -72,7 +72,7 @@ class EditionTable(gtk.Table):
         name_label = gtk.Label('name:')
         self.name_field = name_entry = gtk.Entry()
         self.attach(name_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
-        self.attach(name_entry, 1, 3, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(name_entry, 1, 3, 0, 1, yoptions = gtk.FILL)
 
         value_label = gtk.Label('value:')
         self.value_field = value_entry = gtk.Entry()
@@ -82,7 +82,7 @@ class EditionTable(gtk.Table):
         value_checkbutton.connect(
             'toggled',
             lambda button: value_entry.set_editable(button.get_active()))
-        self.attach(value_label, 0, 1, 1, 2, yoptions = gtk.FILL)
+        self.attach(value_label, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
         self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
         self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
 
@@ -108,6 +108,11 @@ class EditionTable(gtk.Table):
         attributes_label = gtk.Label('attributes:')
         attributes_list = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING,
                                                      gobject.TYPE_STRING))
+        attributes_scroll = gtk.ScrolledWindow()
+        attributes_scroll.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
+        attributes_scroll.set_shadow_type(gtk.SHADOW_OUT)
+        attributes_scroll.set_border_width(5)
+        attributes_scroll.add(attributes_list)
         self.attributes_field = attributes_model = attributes_list.get_model()
 
         def edited_handler(cell, path, text, data):
@@ -132,7 +137,7 @@ class EditionTable(gtk.Table):
         attributes_list.append_column(variable_column)
         attributes_list.append_column(value_column)
         self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
-        self.attach(attributes_list, 0, 3, 7, 8, yoptions = gtk.FILL)
+        self.attach(attributes_scroll, 0, 3, 7, 8)
 
     def clear(self):
         self.name_field.set_text('')
@@ -140,7 +145,7 @@ class EditionTable(gtk.Table):
         self.value_check_field.set_active(False)
         self.attributes_field.clear()
 
-    def cursor_changed(self, tree):
+    def save_changed(self, tree):
         if self.last_selected is not None:
             attributes = {}
             i = self.attributes_field.iter_children(None)
@@ -153,6 +158,9 @@ class EditionTable(gtk.Table):
                 self.value_check_field.get_active(),
                 attributes, )
             
+    def cursor_changed(self, tree):
+        self.save_changed(tree)
+            
         self.clear()
         
         self.last_selected = current = self.get_selected(tree)
@@ -382,7 +390,12 @@ class PyGTKControl():
             tree_column.add_attribute(tree_renderer, 'text', 0)
             tree.append_column(tree_column)
             
-            panels.pack_start(tree, expand = False)
+            tree_scroll = gtk.ScrolledWindow()
+            tree_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tree_scroll.set_shadow_type(gtk.SHADOW_OUT)
+            tree_scroll.set_border_width(5)
+            tree_scroll.add(tree)
+            panels.pack_start(tree_scroll)
             panels.pack_start(EditionTable(tree, definitions))
             
             for option in options:



commit b5bafeea7a86257a1cce9938b0279c9334f8128d
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 19 23:21:58 2009 +0200

    Save attributes on tree selection change.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 31089ce..2094b9d 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -142,10 +142,16 @@ class EditionTable(gtk.Table):
 
     def cursor_changed(self, tree):
         if self.last_selected is not None:
+            attributes = {}
+            i = self.attributes_field.iter_children(None)
+            while i is not None:
+                attributes[self.attributes_field.get_value(i, 0)] = \
+                    self.attributes_field.get_value(i, 1)
+                i = self.attributes_field.iter_next(i)
             self.definitions[self.last_selected] = (
                 self.value_field.get_text(),
                 self.value_check_field.get_active(),
-                {}, ) # add attributes
+                attributes, )
             
         self.clear()
         
@@ -153,7 +159,8 @@ class EditionTable(gtk.Table):
         value, value_check, attributes = self.definitions[current]
         self.value_field.set_text(value)
         self.value_check_field.set_active(value_check)
-        # add attributes
+        for attribute in attributes:
+            self.attributes_field.append((attribute, attributes[attribute], ))
 
     def get_selected(self, tree):
         model, selected = tree.get_selection().get_selected()



commit c155a354bc98d44543eb748b8e9d16d44540b99d
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 19 22:07:23 2009 +0200

    Partial transfer to treeview.
    
    Issues:
    - no reading, writing, new file
    - linear searching on every select
    - attributes are not remembered

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 6045048..31089ce 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -61,6 +61,109 @@ class Connection():
     def get_password(self):
         return self.widgets.get_widget('password_entry').get_text()
 
+class EditionTable(gtk.Table):
+    def __init__(self, tree, definitions):
+        gtk.Table.__init__(self, 8, 3)
+        
+        tree.connect('cursor-changed', self.cursor_changed)
+        self.definitions = definitions
+        self.last_selected = None
+
+        name_label = gtk.Label('name:')
+        self.name_field = name_entry = gtk.Entry()
+        self.attach(name_label, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        self.attach(name_entry, 1, 3, 0, 1, gtk.FILL, gtk.FILL)
+
+        value_label = gtk.Label('value:')
+        self.value_field = value_entry = gtk.Entry()
+        self.value_check_field = value_checkbutton = gtk.CheckButton()
+        value_checkbutton.set_tooltip_text('If active value will be used.')
+        value_checkbutton.set_active(True)
+        value_checkbutton.connect(
+            'toggled',
+            lambda button: value_entry.set_editable(button.get_active()))
+        self.attach(value_label, 0, 1, 1, 2, yoptions = gtk.FILL)
+        self.attach(value_entry, 1, 2, 1, 2, yoptions = gtk.FILL)
+        self.attach(value_checkbutton, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
+
+        add_definition_button = gtk.Button('add sub-definition')
+        self.attach(add_definition_button, 0, 3, 2, 3, yoptions = gtk.FILL)
+        
+        remove_definition_button = gtk.Button('remove this definition')
+        self.attach(remove_definition_button, 0, 3, 3, 4, yoptions = gtk.FILL)
+
+        add_attribute_button = gtk.Button('add attribute')
+        remove_attribute_button = gtk.Button('remove attribute')
+        add_attribute_button.connect(
+            'clicked',
+            lambda button: attributes_model.append(('', '', )))
+        def remove_attribute(button):
+            model, selected = attributes_list.get_selection().get_selected()
+            if selected is not None:
+                model.remove(selected)
+        remove_attribute_button.connect('clicked', remove_attribute)
+        self.attach(add_attribute_button, 0, 3, 4, 5, yoptions = gtk.FILL)
+        self.attach(remove_attribute_button, 0, 3, 5, 6, yoptions = gtk.FILL)
+
+        attributes_label = gtk.Label('attributes:')
+        attributes_list = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING,
+                                                     gobject.TYPE_STRING))
+        self.attributes_field = attributes_model = attributes_list.get_model()
+
+        def edited_handler(cell, path, text, data):
+            model, col = data
+            model[path][col] = text
+        variable_column = gtk.TreeViewColumn('variable')
+        variable_renderer = gtk.CellRendererText()
+        variable_renderer.set_property('editable', True)
+        variable_renderer.connect('edited', edited_handler,
+                                  (attributes_model, 0, ))
+        variable_column.pack_start(variable_renderer)
+        variable_column.add_attribute(variable_renderer, 'text', 0)
+        
+        value_column = gtk.TreeViewColumn('value')
+        value_renderer = gtk.CellRendererText()
+        value_renderer.set_property('editable', True)
+        value_renderer.connect('edited', edited_handler,
+                                  (attributes_model, 1, ))
+        value_column.pack_start(value_renderer)
+        value_column.add_attribute(value_renderer, 'text', 1)
+        
+        attributes_list.append_column(variable_column)
+        attributes_list.append_column(value_column)
+        self.attach(attributes_label, 0, 3, 6, 7, yoptions = gtk.FILL)
+        self.attach(attributes_list, 0, 3, 7, 8, yoptions = gtk.FILL)
+
+    def clear(self):
+        self.name_field.set_text('')
+        self.value_field.set_text('')
+        self.value_check_field.set_active(False)
+        self.attributes_field.clear()
+
+    def cursor_changed(self, tree):
+        if self.last_selected is not None:
+            self.definitions[self.last_selected] = (
+                self.value_field.get_text(),
+                self.value_check_field.get_active(),
+                {}, ) # add attributes
+            
+        self.clear()
+        
+        self.last_selected = current = self.get_selected(tree)
+        value, value_check, attributes = self.definitions[current]
+        self.value_field.set_text(value)
+        self.value_check_field.set_active(value_check)
+        # add attributes
+
+    def get_selected(self, tree):
+        model, selected = tree.get_selection().get_selected()
+        current = None
+        for it in self.definitions:
+            if model[it].path == model[selected].path:
+                current = it
+                break
+        return current
+    
 class PyGTKControl():
     '''GNU MyServer Control main window.'''
 
@@ -243,12 +346,6 @@ class PyGTKControl():
 
     def construct_options(self):
         '''Reads known options from file and prepares GUI.'''
-        def make_tab_name(text):
-            return text.capitalize().replace('.', ' ').replace('_', ' ')
-        def make_name(text, tab):
-            if tab == 'other':
-                return text
-            return text[len(tab):].replace('.', ' ').replace('_', ' ').strip()
 
         # segregate options by tab
         segregated_options = {}
@@ -262,63 +359,37 @@ class PyGTKControl():
                 segregated_options[tab] = []
             segregated_options[tab].append(option)
 
-        tabs = {}
-        self.options = {} # option name => (CheckButton, field, type, )
+        tabs = {} # tab => definitions
         for tab in GUIConfig.tabs + ['other', 'unknown']:
+            definitions = {} # TreeIterator => (value, value_check, 
attributes, )
+            tabs[tab] = definitions
             options = segregated_options.get(tab, [])
-            tabs[tab] = gtk.Table(max(1, len(options)), 3)
-            for i in xrange(len(options)):
-                option = options[i]
-                text, var = GUIConfig.options[option]
-                label = gtk.Label(make_name(option, tab))
-                label.set_tooltip_text(text)
-                check = gtk.CheckButton()
-                if var == 'string':
-                    field = gtk.Entry()
-                    self.options[option] = (check, field, 'string', )
-                elif var == 'integer':
-                    field = gtk.SpinButton(gtk.Adjustment(
-                            0, 0, 2147483647, 1))
-                    self.options[option] = (check, field, 'integer', )
-                elif var == 'bool':
-                    field = gtk.combo_box_new_text()
-                    field.append_text('yes')
-                    field.append_text('no')
-                    field.set_active(0)
-                    self.options[option] = (check, field, 'bool', )
-                elif var == 'list':
-                    tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
-                    tree.set_headers_visible(False)
-                    tree_column = gtk.TreeViewColumn()
-                    tree_renderer = gtk.CellRendererText()
-                    tree_column.pack_start(tree_renderer)
-                    tree_column.add_attribute(tree_renderer, 'text', 0)
-                    tree.append_column(tree_column)
-                    new_name = gtk.Entry()
-                    add_button = gtk.Button('add')
-                    remove_button = gtk.Button('remove')
-                    field = gtk.Table(3, 2)
-                    field.attach(new_name, 0, 2, 0, 1)
-                    field.attach(add_button, 0, 1, 1, 2)
-                    field.attach(remove_button, 1, 2, 1, 2)
-                    field.attach(tree, 0, 2, 2, 3)
-                    def add_to_list(button):
-                        tree.get_model().append((new_name.get_text(), ))
-                        new_name.set_text('')
-                    add_button.connect('clicked', add_to_list)
-                    def remove_from_list(button):
-                        model, selected = tree.get_selection().get_selected()
-                        if selected is not None:
-                            model.remove(selected)
-                    remove_button.connect('clicked', remove_from_list)
-                    self.options[option] = (check, tree, 'list', )
-                tabs[tab].attach(check, 0, 1, i, i + 1, gtk.FILL, gtk.FILL)
-                tabs[tab].attach(label, 1, 2, i, i + 1, yoptions = gtk.FILL)
-                tabs[tab].attach(field, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            panels = gtk.HBox()
+            
+            tree = gtk.TreeView(gtk.TreeStore(gobject.TYPE_STRING))
+            tree_model = tree.get_model()
+            tree.set_headers_visible(False)
+            tree_column = gtk.TreeViewColumn()
+            tree_renderer = gtk.CellRendererText()
+            tree_column.pack_start(tree_renderer)
+            tree_column.add_attribute(tree_renderer, 'text', 0)
+            tree.append_column(tree_column)
+            
+            panels.pack_start(tree, expand = False)
+            panels.pack_start(EditionTable(tree, definitions))
+            
+            for option in options:
+                tooltip_text, var = GUIConfig.options[option]
+                tooltip = gtk.Tooltip()
+                tooltip.set_text(tooltip_text)
+                it = tree_model.append(None, (option, ))
+                definitions[it] = ('', False, {})
+                tree.set_tooltip_row(tooltip, tree_model[it].path) # not 
working
+             
             self.widgets.get_widget('notebook').append_page(
-                tabs[tab], gtk.Label(make_tab_name(tab)))
+                panels, gtk.Label(tab))
 
-        self.on_new_menu_item_activate()
+        # self.on_new_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 
 PyGTKControl()



commit e95ef51c3bcc931fd157807e050e4376b6fa2660
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 19 15:45:51 2009 +0200

    Unix installation fixed upstream.

diff --git a/myserver/binaries/MIMEtypes.xml.unix.default 
b/myserver/binaries/MIMEtypes.xml.unix.default
deleted file mode 100755
index f6830f6..0000000
--- a/myserver/binaries/MIMEtypes.xml.unix.default
+++ /dev/null
@@ -1,503 +0,0 @@
-<?xml version="1.0"?>
-
-<MIMES>
-  <MIME mime="text/html" handler="MSCGI" param="">
-    <EXTENSION value="mscgi"/>
-  </MIME>
-  
-  <MIME mime="text/html" handler="CGI" self="YES" param="">
-    <EXTENSION value="cgi"/>
-  </MIME>
-  
-  <MIME mime="text/html" handler="ISAPI" param="">
-    <EXTENSION value="isapi"/>
-  </MIME>
-  
-  <MIME mime="text/html" handler="SCGI" self="YES" param="">
-    <EXTENSION value="scgi"/>
-  </MIME>
-
-  <MIME mime="text/html" handler="FASTCGI" self="YES" param="">
-    <EXTENSION value="fcgi"/>
-  </MIME>
-  
-  <MIME mime="text/html" handler="WINCGI" param="">
-    <EXTENSION value="wincgi"/>
-  </MIME>
-
-
-  <MIME mime="application/postscript" handler="SEND" param="">
-    <EXTENSION value="ai"/>
-    <EXTENSION value="eps"/>
-    <EXTENSION value="ps"/>
-  </MIME>
-  
-  <MIME mime="video/quicktime" handler="SEND" param="">
-    <EXTENSION value="mov"/>
-    <EXTENSION value="qt"/>
-  </MIME>
-
-  <MIME mime="image/png" handler="SEND" param="">
-    <EXTENSION value="png"/>
-  </MIME>
-
-  <MIME mime="video/vndvivo" handler="SEND" param="">
-    <EXTENSION value="viv"/>
-    <EXTENSION value="vivo"/>
-  </MIME>
-
-  <MIME mime="application/xtex" handler="SEND" param="">
-    <EXTENSION value="tex"/>
-  </MIME>
-
-  <MIME mime="text/xsetext" handler="SEND" param="">
-    <EXTENSION value="etx"/>
-  </MIME>
-
-  <MIME mime="application/vda" handler="SEND" param="">
-    <EXTENSION value="vda"/>
-  </MIME>
-
-  <MIME mime="application/dsptype" handler="SEND" param="">
-    <EXTENSION value="tsp"/>
-  </MIME>
-
-  <MIME mime="application/xstuffit" handler="SEND" param="">
-    <EXTENSION value="sit"/>
-  </MIME>
-
-  <MIME mime="application/solids" handler="SEND" param="">
-    <EXTENSION value="sol"/>
-  </MIME>
-
-  <MIME mime="application/xfuturesplash" handler="SEND" param="">
-    <EXTENSION value="spl"/>
-  </MIME>
-
-  <MIME mime="application/xbcpio" handler="SEND" param="">
-    <EXTENSION value="bcpio"/>
-  </MIME>
-
-  <MIME mime="application/oda" handler="SEND" param="">
-    <EXTENSION value="oda"/>
-  </MIME>
-
-  <MIME mime="application/pro_eng" handler="SEND" param="">
-    <EXTENSION value="prt"/>
-  </MIME>
-
-  <MIME mime="model/iges" handler="SEND" param="">
-    <EXTENSION value="iges"/>
-    <EXTENSION value="igs"/>
-  </MIME>
-
-  <MIME mime="audio/TSPaudio" handler="SEND" param="">
-    <EXTENSION value="tsi"/>
-  </MIME>
-
-  <MIME mime="image/xportablegraymap" handler="SEND" param="">
-    <EXTENSION value="pgm"/>
-  </MIME>
-
-  <MIME mime="application/xlatex" handler="SEND" param="">
-    <EXTENSION value="latex"/>
-  </MIME>
-
-  <MIME mime="application/xsh" handler="SEND" param="">
-    <EXTENSION value="sh"/>
-  </MIME>
-
-  <MIME mime="application/drafting" handler="SEND" param="">
-    <EXTENSION value="drw"/>
-  </MIME>
-
-  <MIME mime="text/html" handler="SEND" param="">
-    <EXTENSION value="htm"/>
-    <EXTENSION value="html"/>
-  </MIME>
-
-  <MIME mime="application/smil" handler="SEND" param="">
-    <EXTENSION value="smi"/>
-    <EXTENSION value="smil"/>
-  </MIME>
-
-  <MIME mime="application/vndmif" handler="SEND" param="">
-    <EXTENSION value="mif"/>
-  </MIME>
-
-  <MIME mime="application/xipix" handler="SEND" param="">
-    <EXTENSION value="ipx"/>
-  </MIME>
-
-  <MIME mime="model/mesh" handler="SEND" param="">
-    <EXTENSION value="mesh"/>
-    <EXTENSION value="msh"/>
-    <EXTENSION value="silo"/>
-  </MIME>
-
-  <MIME mime="application/xtroffman" handler="SEND" param="">
-    <EXTENSION value="man"/>
-  </MIME>
-
-  <MIME mime="application/xcsh" handler="SEND" param="">
-    <EXTENSION value="csh"/>
-  </MIME>
-
-  <MIME mime="audio/xpnrealaudioplugin" handler="SEND" param="">
-    <EXTENSION value="rpm"/>
-  </MIME>
-
-  <MIME mime="image/cmuraster" handler="SEND" param="">
-    <EXTENSION value="ras"/>
-  </MIME>
-
-  <MIME mime="www/mime" handler="SEND" param="">
-    <EXTENSION value="mime"/>
-  </MIME>
-
-  <MIME mime="application/xdirector" handler="SEND" param="">
-    <EXTENSION value="dcr"/>
-    <EXTENSION value="dir"/>
-    <EXTENSION value="dxr"/>
-  </MIME>
-
-  <MIME mime="text/xml" handler="SEND" param="">
-    <EXTENSION value="xml"/>
-  </MIME>
-
-  <MIME mime="application/xfreelance" handler="SEND" param="">
-    <EXTENSION value="pre"/>
-  </MIME>
-
-  <MIME mime="image/ief" handler="SEND" param="">
-    <EXTENSION value="ief"/>
-  </MIME>
-
-  <MIME mime="application/xshar" handler="SEND" param="">
-    <EXTENSION value="shar"/>
-  </MIME>
-
-  <MIME mime="audio/xrealaudio" handler="SEND" param="">
-    <EXTENSION value="ra"/>
-  </MIME>
-
-  <MIME mime="application/mspowerpoint" handler="SEND" param="">
-    <EXTENSION value="pot"/>
-    <EXTENSION value="pps"/>
-    <EXTENSION value="ppt"/>
-    <EXTENSION value="ppz"/>
-  </MIME>
-
-  <MIME mime="audio/midi" handler="SEND" param="">
-    <EXTENSION value="kar"/>
-    <EXTENSION value="mid"/>
-    <EXTENSION value="midi"/>
-  </MIME>
-
-  <MIME mime="image/jpeg" handler="SEND" param="">
-    <EXTENSION value="jpe"/>
-    <EXTENSION value="jpeg"/>
-    <EXTENSION value="jpg"/>
-  </MIME>
-
-  <MIME mime="application/xkoan" handler="SEND" param="">
-    <EXTENSION value="skd"/>
-    <EXTENSION value="skm"/>
-    <EXTENSION value="skp"/>
-    <EXTENSION value="skt"/>
-  </MIME>
-
-  <MIME mime="application/SLA" handler="SEND" param="">
-    <EXTENSION value="stl"/>
-  </MIME>
-
-  <MIME mime="application/STEP" handler="SEND" param="">
-    <EXTENSION value="step"/>
-    <EXTENSION value="stp"/>
-  </MIME>
-
-  <MIME mime="application/xshockwaveflash" handler="SEND" param="">
-    <EXTENSION value="swf"/>
-  </MIME>
-
-  <MIME mime="image/xxwindowdump" handler="SEND" param="">
-    <EXTENSION value="xwd"/>
-  </MIME>
-
-  <MIME mime="image/xxbitmap" handler="SEND" param="">
-    <EXTENSION value="xbm"/>
-  </MIME>
-
-  <MIME mime="application/dxf" handler="SEND" param="">
-    <EXTENSION value="dxf"/>
-  </MIME>
-
-  <MIME mime="text/sgml" handler="SEND" param="">
-    <EXTENSION value="sgm"/>
-    <EXTENSION value="sgml"/>
-  </MIME>
-
-  <MIME mime="model/vrml" handler="SEND" param="">
-    <EXTENSION value="vrml"/>
-    <EXTENSION value="wrl"/>
-  </MIME>
-
-  <MIME mime="application/xipscript" handler="SEND" param="">
-    <EXTENSION value="ips"/>
-  </MIME>
-
-  <MIME mime="image/xportablepixmap" handler="SEND" param="">
-    <EXTENSION value="ppm"/>
-  </MIME>
-
-  <MIME mime="text/plain" handler="SEND" param="">
-    <EXTENSION value="asc"/>
-    <EXTENSION value="c"/>
-    <EXTENSION value="cc"/>
-    <EXTENSION value="f"/>
-    <EXTENSION value="f90"/>
-    <EXTENSION value="h"/>
-    <EXTENSION value="hh"/>
-    <EXTENSION value="m"/>
-    <EXTENSION value="txt"/>
-  </MIME>
-
-  <MIME mime="application/set" handler="SEND" param="">
-    <EXTENSION value="set"/>
-  </MIME>
-
-  <MIME mime="video/xmsvideo" handler="SEND" param="">
-    <EXTENSION value="avi"/>
-  </MIME>
-
-  <MIME mime="audio/xwav" handler="SEND" param="">
-    <EXTENSION value="wav"/>
-  </MIME>
-
-  <MIME mime="application/xgtar" handler="SEND" param="">
-    <EXTENSION value="gtar"/>
-  </MIME>
-
-  <MIME mime="application/xwaissource" handler="SEND" param="">
-    <EXTENSION value="src"/>
-  </MIME>
-
-  <MIME mime="application/acad" handler="SEND" param="">
-    <EXTENSION value="dwg"/>
-  </MIME>
-
-  <MIME mime="application/xlisp" handler="SEND" param="">
-    <EXTENSION value="lsp"/>
-  </MIME>
-
-  <MIME mime="application/zip" handler="SEND" param="">
-    <EXTENSION value="zip"/>
-  </MIME>
-
-  <MIME mime="audio/xpnrealaudio" handler="SEND" param="">
-    <EXTENSION value="ram"/>
-    <EXTENSION value="rm"/>
-  </MIME>
-
-  <MIME mime="text/tabseparatedvalues" handler="SEND" param="">
-    <EXTENSION value="tsv"/>
-  </MIME>
-
-  <MIME mime="image/xxpixmap" handler="SEND" param="">
-    <EXTENSION value="xpm"/>
-  </MIME>
-
-  <MIME mime="application/xtexinfo" handler="SEND" param="">
-    <EXTENSION value="texi"/>
-    <EXTENSION value="texinfo"/>
-  </MIME>
-
-  <MIME mime="application/xustar" handler="SEND" param="">
-    <EXTENSION value="ustar"/>
-  </MIME>
-
-  <MIME mime="application/xcpio" handler="SEND" param="">
-    <EXTENSION value="cpio"/>
-  </MIME>
-
-  <MIME mime="application/vndmsexcel" handler="SEND" param="">
-    <EXTENSION value="xlc"/>
-    <EXTENSION value="xll"/>
-    <EXTENSION value="xlm"/>
-    <EXTENSION value="xls"/>
-    <EXTENSION value="xlw"/>
-  </MIME>
-
-  <MIME mime="image/xrgb" handler="SEND" param="">
-    <EXTENSION value="rgb"/>
-  </MIME>
-
-  <MIME mime="image/xportablebitmap" handler="SEND" param="">
-    <EXTENSION value="pbm"/>
-  </MIME>
-
-  <MIME mime="application/pdf" handler="SEND" param="">
-    <EXTENSION value="pdf"/>
-  </MIME>
-
-  <MIME mime="application/xjavascript" handler="SEND" param="">
-    <EXTENSION value="js"/>
-  </MIME>
-
-  <MIME mime="application/xcdlink" handler="SEND" param="">
-    <EXTENSION value="vcd"/>
-  </MIME>
-
-  <MIME mime="chemical/xpdb" handler="SEND" param="">
-    <EXTENSION value="pdb"/>
-    <EXTENSION value="xyz"/>
-  </MIME>
-
-  <MIME mime="application/xsv4crc" handler="SEND" param="">
-    <EXTENSION value="sv4crc"/>
-  </MIME>
-
-  <MIME mime="image/gif" handler="SEND" param="">
-    <EXTENSION value="gif"/>
-  </MIME>
-
-  <MIME mime="application/xlotusscreencam" handler="SEND" param="">
-    <EXTENSION value="scm"/>
-  </MIME>
-
-  <MIME mime="image/tiff" handler="SEND" param="">
-    <EXTENSION value="tif"/>
-    <EXTENSION value="tiff"/>
-  </MIME>
-
-  <MIME mime="application/maccompactpro" handler="SEND" param="">
-    <EXTENSION value="cpt"/>
-  </MIME>
-
-  <MIME mime="application/clariscad" handler="SEND" param="">
-    <EXTENSION value="ccad"/>
-  </MIME>
-
-  <MIME mime="video/mpeg" handler="SEND" param="">
-    <EXTENSION value="mpe"/>
-    <EXTENSION value="mpeg"/>
-    <EXTENSION value="mpg"/>
-  </MIME>
-
-  <MIME mime="application/xtcl" handler="SEND" param="">
-    <EXTENSION value="tcl"/>
-  </MIME>
-
-  <MIME mime="application/xhdf" handler="SEND" param="">
-    <EXTENSION value="hdf"/>
-  </MIME>
-
-  <MIME mime="text/css" handler="SEND" param="">
-    <EXTENSION value="css"/>
-  </MIME>
-
-  <MIME mime="application/xtroff" handler="SEND" param="">
-    <EXTENSION value="roff"/>
-    <EXTENSION value="t"/>
-    <EXTENSION value="tr"/>
-  </MIME>
-
-  <MIME mime="application/macbinhex40" handler="SEND" param="">
-    <EXTENSION value="hqx"/>
-  </MIME>
-
-  <MIME mime="application/ideas" handler="SEND" param="">
-    <EXTENSION value="unv"/>
-  </MIME>
-
-  <MIME mime="audio/xaiff" handler="SEND" param="">
-    <EXTENSION value="aif"/>
-    <EXTENSION value="aifc"/>
-    <EXTENSION value="aiff"/>
-  </MIME>
-
-  <MIME mime="application/xdvi" handler="SEND" param="">
-    <EXTENSION value="dvi"/>
-  </MIME>
-
-  <MIME mime="application/andrewinset" handler="SEND" param="">
-    <EXTENSION value="ez"/>
-  </MIME>
-
-  <MIME mime="application/xnetcdf" handler="SEND" param="">
-    <EXTENSION value="cdf"/>
-    <EXTENSION value="nc"/>
-  </MIME>
-
-  <MIME mime="application/xsv4cpio" handler="SEND" param="">
-    <EXTENSION value="sv4cpio"/>
-  </MIME>
-
-  <MIME mime="video/xsgimovie" handler="SEND" param="">
-    <EXTENSION value="movie"/>
-  </MIME>
-
-  <MIME mime="audio/mpeg" handler="SEND" param="">
-    <EXTENSION value="mp2"/>
-    <EXTENSION value="mp3"/>
-    <EXTENSION value="mpga"/>
-  </MIME>
-
-  <MIME mime="application/xtroffme" handler="SEND" param="">
-    <EXTENSION value="me"/>
-  </MIME>
-
-  <MIME mime="xconference/xcooltalk" handler="SEND" param="">
-    <EXTENSION value="ice"/>
-  </MIME>
-
-  <MIME mime="application/octetstream" handler="SEND" param="">
-    <EXTENSION value="bin"/>
-    <EXTENSION value="class"/>
-    <EXTENSION value="dms"/>
-    <EXTENSION value="exe"/>
-    <EXTENSION value="lha"/>
-    <EXTENSION value="lzh"/>
-  </MIME>
-
-  <MIME mime="application/xtroffms" handler="SEND" param="">
-    <EXTENSION value="ms"/>
-  </MIME>
-
-  <MIME mime="application/xgzip" handler="SEND" param="">
-    <EXTENSION value="gz"/>
-  </MIME>
-
-  <MIME mime="audio/basic" handler="SEND" param="">
-    <EXTENSION value="au"/>
-    <EXTENSION value="snd"/>
-  </MIME>
-
-  <MIME mime="application/xtar" handler="SEND" param="">
-    <EXTENSION value="tar"/>
-  </MIME>
-
-  <MIME mime="text/rtf" handler="SEND" param="">
-    <EXTENSION value="rtf"/>
-  </MIME>
-
-  <MIME mime="text/richtext" handler="SEND" param="">
-    <EXTENSION value="rtx"/>
-  </MIME>
-
-  <MIME mime="image/xportableanymap" handler="SEND" param="">
-    <EXTENSION value="pnm"/>
-  </MIME>
-
-  <MIME mime="application/xchesspgn" handler="SEND" param="">
-    <EXTENSION value="pgn"/>
-  </MIME>
-
-  <MIME mime="video/xfli" handler="SEND" param="">
-    <EXTENSION value="fli"/>
-  </MIME>
-
-  <MIME mime="application/msword" handler="SEND" param="">
-    <EXTENSION value="doc"/>
-  </MIME>
-</MIMES>
diff --git a/myserver/binaries/myserver.xml.unix.default 
b/myserver/binaries/myserver.xml.unix.default
deleted file mode 100755
index 77f11ad..0000000
--- a/myserver/binaries/myserver.xml.unix.default
+++ /dev/null
@@ -1,122 +0,0 @@
-<?xml version="1.0"?>
-
-<!--MODIFY THE FILE ONLY IF YOU ARE SURE OF WHAT YOU ARE DOING-->
-<MYSERVER>
-
-       <!--CHOOSE THE LANGUAGE TO USE FOR THE SERVER-->
-       <DEFINE name="server.language" value="english.xml" />
-
-       <!--VERBOSITY ON LOG FILE-->
-       <DEFINE name="server.verbosity" value="2" />
-
-       <!--NUMBER OF SERVING THREADS ALWAYS ACTIVE-->
-  <DEFINE name="server.static_threads" value="5" />
-
-       <!--MAXIMUM NUMBER OF SERVING THREADS THAT THE SCHEDULER CAN
-                       CREATE-->
-       <DEFINE name="server.max_threads" value="200" />
-
-       <!--DIMENSION OF EVERY BUFFER IN BYTES IF ON THE MACHINE THERE IS A
-                       LOT OF RAM      THIS VALUES CAN BE INCREASED TO MAKE 
MYSERVER
-                       FASTER. NORMALLY THE MEMORY USED BY BUFFERS IS EQUAL TO
-                       BUFFER_SIZE * 2 * NUM_OF_THREADS-->
-       <DEFINE name="server.buffer_size" value="102400" />
-
-       <!--DEFAULT FILENAME TO SEND IN A DIRECTORY
-                       IF THE FILE ISN'T IN THE PATH THEN THE DIRECTORY
-                       CONTENT IS SENT -->
-  <DEFINE name="http.default_file">
-    <DEFINE value="default.html"/>
-    <DEFINE value="default.htm"/>
-    <DEFINE value="index.html"/>
-    <DEFINE value="index.htm"/>
-  </DEFINE>
-
-       <!--TIMEOUT OF EVERY CLIENTS CONNECTED TO THE SERVER.
-                       IF THE CLIENT DOESN'T REQUEST ANYTHING FOR N
-                       SECONDS THE CONNECTION WITH THE CLIENT IS
-                       CLOSED. SET THIS TO 0 IF YOU DON'T WANT USE
-                       KEEP-ALIVE CONNECTIONS  -->
-       <DEFINE name="connection.timeout" value="180" />
-
-       <!--SET THIS TO YES FOR USE PERSONALIZED PAGES TO SEND MESSAGE,
-                       PERSONALIZED PAGES ARE IN THE SYSTEM DIRECTORY  -->
-       <DEFINE name="http.use_error_file" value="YES" />
-
-       <!--DEFINE THE MAX NUMBER OF CONNECTIONS TO ALLOW TO THE SERVER. IF
-                       0 THEN INFINITE CONNECTIONS ARE ALLOWED.-->
-       <DEFINE name="server.max_connections" value="0" />
-
-       <!--DEFINE THE MAX NUMBER OF CONNECTIONS TO ACCEPT.-->
-       <DEFINE name="server.max_accepted_connections" value="0" />
-
-  <!--TEXT AND BACKGROUND ATTRIBUTES FOR CONSOLE LOG MESSAGES. -->
-  <DEFINE name="log_color.info_fg" value="white" />
-  <DEFINE name="log_color.info_bg" value="black" />
-
-  <DEFINE name="log_color.warning_fg" value="yellow" />
-  <DEFINE name="log_color.warning_bg" value="black" />
-
-  <DEFINE name="log_color.error_fg" value="red" />
-  <DEFINE name="log_color.error_bg" value="black" />
-
-       <!--MAX SIZE OF THE LOG FILE IN BYTES   -->
-       <DEFINE name="server.max_log_size" value="1048576" />
-
-       <!--DEFINE THE FOLDER BROWSING STYLE-->
-       <DEFINE name="http.dir.css" value="/sys/css/browsestyle.css" />
-
-       <!--DEFINE THE ADMINISTRATOR E-MAIL-->
-       <DEFINE name="server.admin" value="address@hidden" />
-
-       <!--DEFINE THE GZIP COMPRESSION THRESHOLD VALUE-->
-       <DEFINE name="gzip.threshold" value="1048576" />
-
-       <!--DEFINE IF LINKS SHOULD BE FOLLOWED-->
-       <DEFINE name="symlinks.follow" value="YES" />
-
-       <!--MAX CACHE SIZE FOR STATIC FILES-->
-       <DEFINE name="server.max_files_cache" value="10485760" />
-
-       <!--MAX CACHE SIZE FOR SINGLE STATIC FILE-->
-       <DEFINE name="server.max_file_cache" value="1048576" />
-
-       <!--MIN CACHE SIZE FOR SINGLE STATIC FILE-->
-       <DEFINE name="server.min_file_cache" value="1048576" />
-
-       <!-- ENABLE HOME DIRECTORY PER USER -->
-       <DEFINE name="http.use_home_directory" value="NO" />
-
-       <!-- DEFINE NAME FOR HOME DIRECTORY BROWSING, BY DEFAULT THE SERVER
-                        USES public_html -->
-       <DEFINE name="http.home_directory" value="public_html" />
-
-  <!--SET THIS TO YES IF YOU WANT TO ENABLE THE CONTROL PROTOCOL-->
-       <DEFINE name="control.enabled" value="NO" />
-
-       <!--MAXIMUM NUMBER OF EXTERNAL SERVERS THAT CAN BE EXECUTED-->
-       <DEFINE name="server.max_servers" value="100" />
-
-  <!--SET HERE THE NAME FOR THE CONTROL ADMIN. WE SUGGEST YOU YO USE
-      SOMETHING DIFFERENT FROM ADMIN, ADMINISTRATOR OR ROOT-->
-  <DEFINE name="control.admin" value="ADMIN" />
-
-  <!--DEFINE HERE A GOOD STRONG PASSWORD FOR THE ADMIN-->
-  <DEFINE name="control.password" value="PLEASE_CHANGE_THIS_PASSWORD" />
-
-  <!--FTP GLOBAL CONFIGURATION-->
-  <!--ALLOW ANONYMOUS LOGIN-->
-  <DEFINE name="ftp.allow_anonymous" value="NO" />
-
-  <!--IF ANONYMOUS ALLOWED, TELLS IF NEEDS PASS-->
-  <DEFINE name="ftp.anonymous_need_pass" value="NO" />
-
-  <!--ALLOW ASYNCHRONOUS CMDS LIKE Abor, Quit, Stat-->
-  <DEFINE name="ftp.allow_asynchronous_cmds" value="YES" />
-
-  <!--ALLOW CLIENTS TO SEND MORE THAN ONE COMMAND TO SERVER INTO ONE REQUEST-->
-  <DEFINE name="ftp.allow_pipelining" value="NO" />
-
-  <!--ALLOW CLIENTS TO CHANGE SERVER FILES-->
-  <DEFINE name="ftp.allow_store" value="NO" />
-</MYSERVER>
diff --git a/myserver/binaries/virtualhosts.xml.unix.default 
b/myserver/binaries/virtualhosts.xml.unix.default
deleted file mode 100755
index 36b3b47..0000000
--- a/myserver/binaries/virtualhosts.xml.unix.default
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0"?>
-
-<VHOSTS>
-
-       <VHOST>
-               <NAME>Every HTTP connection</NAME>
-               
-               <PORT>80</PORT>
-               <PROTOCOL>HTTP</PROTOCOL>
-               
-               <DOCROOT>web</DOCROOT>
-               <SYSROOT>system</SYSROOT>
-
-               <ACCESSLOG>
-                  <STREAM location="file://logs/MyServerHTTP.log" 
cycle="1048576"/>
-                </ACCESSLOG>
-
-                <WARNINGLOG>
-                  <STREAM location="file://logs/MyServerHTTP.err" 
cycle="1048576"/>
-                </WARNINGLOG>
-       </VHOST>
-
-       <VHOST>
-               <NAME>FTP connection</NAME>
-               
-               <PORT>21</PORT>
-               <PROTOCOL>FTP</PROTOCOL>
-               
-               <DOCROOT>web</DOCROOT>
-               <SYSROOT>system</SYSROOT>
-               
-               <ACCESSLOG>
-                  <STREAM location="file://logs/MyServerFTP.log" 
cycle="1048576"/>
-                </ACCESSLOG>
-
-               <WARNINGLOG>
-                  <STREAM location="file://logs/MyServerFTP.err" 
cycle="1048576"/>
-                </WARNINGLOG>
-       </VHOST>
-</VHOSTS>



commit c3d7e72c5d7a043b32571d78b9dd368f9680722d
Merge: 290fa08 2ada044
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 19 15:45:05 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit 290fa08dae6d19ce02df7a7a13823d5ff319b615
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 18 12:59:43 2009 +0200

    Unknown definitions are not lost in GUI.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 97fd23a..6045048 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public 
License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
+from __future__ import print_function
 import gtk
 import gobject
 import gtk.glade
@@ -42,6 +43,7 @@ class Connection():
         self.widgets.signal_autoconnect(self)
 
     def destroy(self):
+        '''Destroys this widget.'''
         self.widgets.get_widget('connectiondialog').destroy()
         
     def on_cancel_button_clicked(self, widget):
@@ -72,15 +74,19 @@ class PyGTKControl():
         self.controller = None
 
     def on_window_destroy(self, widget):
+        '''Exits program.'''
         gtk.main_quit()
 
     def on_quit_menu_item_activate(self, widget):
+        '''Exits program.'''
         gtk.main_quit()
 
     def on_about_menu_item_activate(self, widget):
+        '''Shows about window.'''
         About()
 
     def on_connect_menu_item_activate(self, widget):
+        '''Show connection dialog.'''
         dialog = Connection()
         def connect(widget):
             self.controller = Controller(
@@ -92,19 +98,23 @@ class PyGTKControl():
         dialog.widgets.get_widget('connectiondialog').show()
 
     def on_disconnect_menu_item_activate(self, widget):
+        '''Disconnect from server.'''
         if self.controller is not None:
             self.controller.disconnect()
         self.controller = None
 
     def on_get_config_menu_item_activate(self, widget):
+        '''Get config from remote server.'''
         if self.controller is not None:
             self.set_up(self.controller.get_server_configuration())
 
     def on_put_config_menu_item_activate(self, widget):
+        '''Put config on remote server.'''
         if self.controller is not None:
             self.controller.put_server_configuration(self.get_current_config())
 
     def on_open_menu_item_activate(self, widget):
+        '''Open local configuration file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
@@ -122,6 +132,7 @@ class PyGTKControl():
         self.chooser.show()
 
     def on_save_as_menu_item_activate(self, widget):
+        '''Save configuration as local file.'''
         if self.chooser is not None:
             self.chooser.destroy()
         self.chooser = gtk.FileChooserDialog(
@@ -138,6 +149,7 @@ class PyGTKControl():
         self.chooser.show()
 
     def on_save_menu_item_activate(self, widget):
+        '''Save configuration to file.'''
         if self.path is None:
             self.on_save_as_menu_item_activate(widget)
         else:
@@ -146,6 +158,7 @@ class PyGTKControl():
                 f.write(str(config))
     
     def get_current_config(self):
+        '''Returns current configuration as list of definitions.'''
         definitions = []
         for option in self.options:
             check, field, var = self.options[option]
@@ -174,11 +187,14 @@ class PyGTKControl():
                 for value in values:
                     definition.add_definition(value)
             definitions.append(definition)
-        return MyServerConfig(definitions)
+        return MyServerConfig(definitions + self.unknown)
 
     def on_new_menu_item_activate(self, widget = None):
+        '''Clears configuration.'''
         if widget is not None:
             self.path = None
+        self.definitions = {} # option name => corresponding Definition
+        self.unknown = [] # list of unknown definitions
         for option in self.options:
             check, field, var = self.options.get(option)
             if var != 'list':
@@ -196,8 +212,10 @@ class PyGTKControl():
                 field.get_model().clear()
 
     def set_up(self, config):
+        '''Reads configuration from given config instance.'''
         def put_to_unknown(definition):
-            pass # TODO
+            self.unknown.append(definition)
+            print('Unknown option:', definition, sep = '\n')
         self.on_new_menu_item_activate()
         for definition in config.get_definitions():
             name = definition.get_name()
@@ -224,6 +242,7 @@ class PyGTKControl():
                         (definition.get_attribute('value'), ))
 
     def construct_options(self):
+        '''Reads known options from file and prepares GUI.'''
         def make_tab_name(text):
             return text.capitalize().replace('.', ' ').replace('_', ' ')
         def make_name(text, tab):
@@ -299,7 +318,6 @@ class PyGTKControl():
             self.widgets.get_widget('notebook').append_page(
                 tabs[tab], gtk.Label(make_tab_name(tab)))
 
-        self.definitions = {} # option name => corresponding Definition
         self.on_new_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 



commit 0162ed2c63fa02058fdcda88c6b15b124b6744a3
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 21:48:22 2009 +0200

    SYSFOLDER => SYSROOT

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index d5e19ae..e6d84f6 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -87,15 +87,15 @@ class VHostTest(unittest.TestCase):
         vhost = VHost(doc_root = '/www')
         self.assertEqual('/www', vhost.get_doc_root())
 
-    def test_sys_folder(self):
+    def test_sys_root(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system')
-        self.assertEqual('/system', vhost.get_sys_folder())
-        vhost.set_sys_folder('/var/system')
-        self.assertEqual('/var/system', vhost.get_sys_folder())
-        vhost.set_sys_folder(None)
-        self.assertEqual(None, vhost.get_sys_folder())
-        vhost = VHost(sys_folder = '/system')
-        self.assertEqual('/system', vhost.get_sys_folder())
+        self.assertEqual('/system', vhost.get_sys_root())
+        vhost.set_sys_root('/var/system')
+        self.assertEqual('/var/system', vhost.get_sys_root())
+        vhost.set_sys_root(None)
+        self.assertEqual(None, vhost.get_sys_root())
+        vhost = VHost(sys_root = '/system')
+        self.assertEqual('/system', vhost.get_sys_root())
 
     def test_logs(self):
         a_log = Log('ACCESSLOG')
@@ -202,7 +202,7 @@ class VHostTest(unittest.TestCase):
         vhost_1.set_doc_root('/var/www')
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost.from_string(str(vhost_0))
-        vhost_1.set_sys_folder('/var/system')
+        vhost_1.set_sys_root('/var/system')
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost.from_string(str(vhost_0))
         vhost_1.add_log(Log('NEWLOG'))
@@ -251,10 +251,10 @@ class VHostTest(unittest.TestCase):
         right = VHost(doc_root = '/www')
         self.assertEqual(vhost, right)
 
-    def test_from_string_sys_folder(self):
-        text = '<VHOST><SYSFOLDER>/system</SYSFOLDER></VHOST>'
+    def test_from_string_sys_root(self):
+        text = '<VHOST><SYSROOT>/system</SYSROOT></VHOST>'
         vhost = VHost.from_string(text)
-        right = VHost(sys_folder = '/system')
+        right = VHost(sys_root = '/system')
         self.assertEqual(vhost, right)
 
     def test_from_string_logs(self):
@@ -293,7 +293,7 @@ class VHostTest(unittest.TestCase):
   <PORT>80</PORT>
   <PROTOCOL>HTTP</PROTOCOL>
   <DOCROOT>/www</DOCROOT>
-  <SYSFOLDER>/system</SYSFOLDER>
+  <SYSROOT>/system</SYSROOT>
   <ACCESSLOG />
   <WARNINGLOG />
   <IP>127.0.0.0/8</IP>
@@ -342,9 +342,9 @@ class VHostTest(unittest.TestCase):
         self.assertEqual(vhost, right)
 
     def test_from_lxml_sys_folder(self):
-        text = '<VHOST><SYSFOLDER>/system</SYSFOLDER></VHOST>'
+        text = '<VHOST><SYSROOT>/system</SYSROOT></VHOST>'
         vhost = VHost.from_lxml_element(etree.XML(text))
-        right = VHost(sys_folder = '/system')
+        right = VHost(sys_root = '/system')
         self.assertEqual(vhost, right)
 
     def test_from_lxml_logs(self):
@@ -383,7 +383,7 @@ class VHostTest(unittest.TestCase):
   <PORT>80</PORT>
   <PROTOCOL>HTTP</PROTOCOL>
   <DOCROOT>/www</DOCROOT>
-  <SYSFOLDER>/system</SYSFOLDER>
+  <SYSROOT>/system</SYSROOT>
   <ACCESSLOG />
   <WARNINGLOG />
   <IP>127.0.0.0/8</IP>
@@ -432,8 +432,8 @@ class VHostTest(unittest.TestCase):
         copy = VHost.from_string(str(vhost))
         self.assertEqual(vhost, copy)
 
-    def test_to_string_sys_folder(self):
-        vhost = VHost(doc_root = '/system')
+    def test_to_string_sys_root(self):
+        vhost = VHost(sys_root = '/system')
         copy = VHost.from_string(str(vhost))
         self.assertEqual(vhost, copy)
 
@@ -496,8 +496,8 @@ class VHostTest(unittest.TestCase):
         copy = VHost.from_lxml_element(vhost.to_lxml_element())
         self.assertEqual(vhost, copy)
 
-    def test_to_lxml_sys_folder(self):
-        vhost = VHost(doc_root = '/system')
+    def test_to_lxml_sys_root(self):
+        vhost = VHost(sys_root = '/system')
         copy = VHost.from_lxml_element(vhost.to_lxml_element())
         self.assertEqual(vhost, copy)
 
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index d1bd76a..54b76f2 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -21,7 +21,7 @@ from log import Log
 
 class VHost():
     def __init__(self, name = None, port = None, protocol = None,
-                 doc_root = None, sys_folder = None, logs = [],
+                 doc_root = None, sys_root = None, logs = [],
                  ip = [], host = {}, private_key = None, certificate = None):
         '''Create new instance of VHost. logs and ip are expected to be a
         collection and host is expected to be a dict {name: useRegex} where 
None
@@ -30,7 +30,7 @@ class VHost():
         self.set_port(port)
         self.set_protocol(protocol)
         self.set_doc_root(doc_root)
-        self.set_sys_folder(sys_folder)
+        self.set_sys_root(sys_root)
         self.logs = []
         for log in logs:
             self.add_log(log)
@@ -47,7 +47,7 @@ class VHost():
         return isinstance(other, VHost) and self.name == other.name and \
             self.port == other.port and self.protocol == other.protocol and \
             self.doc_root == other.doc_root and \
-            self.sys_folder == other.sys_folder and \
+            self.sys_root == other.sys_root and \
             self.logs == other.logs and self.ip == other.ip and \
             self.host == other.host and \
             self.private_key == other.private_key and \
@@ -69,13 +69,13 @@ class VHost():
         '''Set VHost doc root.'''
         self.doc_root = doc_root
 
-    def get_sys_folder(self):
-        '''Get VHost sys folder.'''
-        return self.sys_folder
+    def get_sys_root(self):
+        '''Get VHost sys root.'''
+        return self.sys_root
 
-    def set_sys_folder(self, sys_folder):
-        '''Set VHost sys folder.'''
-        self.sys_folder = sys_folder
+    def set_sys_root(self, sys_root):
+        '''Set VHost sys root.'''
+        self.sys_root = sys_root
 
     def get_protocol(self):
         '''Get VHost protocol.'''
@@ -174,8 +174,8 @@ class VHost():
             root.append(make_element('PROTOCOL', self.protocol))
         if self.doc_root is not None:
             root.append(make_element('DOCROOT', self.doc_root))
-        if self.sys_folder is not None:
-            root.append(make_element('SYSFOLDER', self.sys_folder))
+        if self.sys_root is not None:
+            root.append(make_element('SYSROOT', self.sys_root))
         if self.private_key is not None:
             root.append(make_element('SSL_PRIVATEKEY', self.private_key))
         if self.certificate is not None:
@@ -200,7 +200,7 @@ class VHost():
         port = None
         protocol = None
         doc_root = None
-        sys_folder = None
+        sys_root = None
         private_key = None
         certificate = None
         ip = []
@@ -215,8 +215,8 @@ class VHost():
                 protocol = child.text
             elif child.tag == 'DOCROOT':
                 doc_root = child.text
-            elif child.tag == 'SYSFOLDER':
-                sys_folder = child.text
+            elif child.tag == 'SYSROOT':
+                sys_root = child.text
             elif child.tag == 'IP':
                 ip.append(child.text)
             elif child.tag == 'HOST':
@@ -230,7 +230,7 @@ class VHost():
                 certificate = child.text
             else:
                 logs.append(Log.from_lxml_element(child))
-        return VHost(name, port, protocol, doc_root, sys_folder, logs, ip, 
host,
+        return VHost(name, port, protocol, doc_root, sys_root, logs, ip, host,
                      private_key, certificate)
     
     @staticmethod
diff --git a/myserver/binaries/virtualhosts.xml.unix.default 
b/myserver/binaries/virtualhosts.xml.unix.default
index 3d1897b..36b3b47 100755
--- a/myserver/binaries/virtualhosts.xml.unix.default
+++ b/myserver/binaries/virtualhosts.xml.unix.default
@@ -9,7 +9,7 @@
                <PROTOCOL>HTTP</PROTOCOL>
                
                <DOCROOT>web</DOCROOT>
-               <SYSFOLDER>system</SYSFOLDER>
+               <SYSROOT>system</SYSROOT>
 
                <ACCESSLOG>
                   <STREAM location="file://logs/MyServerHTTP.log" 
cycle="1048576"/>
@@ -27,7 +27,7 @@
                <PROTOCOL>FTP</PROTOCOL>
                
                <DOCROOT>web</DOCROOT>
-               <SYSFOLDER>system</SYSFOLDER>
+               <SYSROOT>system</SYSROOT>
                
                <ACCESSLOG>
                   <STREAM location="file://logs/MyServerFTP.log" 
cycle="1048576"/>



commit fec75c5b9759f29b5a467ee8b32655f864bc82bf
Merge: 121d46c 7f0c89b
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 21:35:10 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit 121d46c683a2a0abf3b6d51a50082aeaf6f1f006
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 21:33:51 2009 +0200

    Perserve additional attributes of definitions.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index e47a67b..97fd23a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -151,17 +151,14 @@ class PyGTKControl():
             check, field, var = self.options[option]
             if not check.get_active():
                 continue
+            definition = self.definitions.get(option)
             if var == 'string':
-                definitions.append(
-                    DefinitionElement(option, {'value': field.get_text()}))
+                definition.set_attribute('value', field.get_text())
             elif var == 'integer':
-                value = str(int(field.get_value()))
-                definitions.append(
-                    DefinitionElement(option, {'value': value}))
+                definition.set_attribute('value', str(int(field.get_value())))
             elif var == 'bool':
-                value = 'YES' if field.get_active() else 'NO'
-                definitions.append(
-                    DefinitionElement(option, {'value': value}))
+                value = 'NO' if field.get_active() else 'YES'
+                definition.set_attribute('value', value)
             elif var == 'list':
                 values = []
                 model = field.get_model()
@@ -172,14 +169,22 @@ class PyGTKControl():
                 values = map(
                     lambda v: DefinitionElement(attributes = {'value': v}),
                     values)
-                definitions.append(
-                    DefinitionTree(option, values))
+                for i in xrange(len(definition.get_definitions())):
+                    definition.remove_definition(0)
+                for value in values:
+                    definition.add_definition(value)
+            definitions.append(definition)
         return MyServerConfig(definitions)
 
     def on_new_menu_item_activate(self, widget = None):
         if widget is not None:
             self.path = None
-        for check, field, var in self.options.itervalues():
+        for option in self.options:
+            check, field, var = self.options.get(option)
+            if var != 'list':
+                self.definitions[option] = DefinitionElement(option)
+            else:
+                self.definitions[option] = DefinitionTree(option)
             check.set_active(False)
             if var == 'string':
                 field.set_text('')
@@ -191,12 +196,20 @@ class PyGTKControl():
                 field.get_model().clear()
 
     def set_up(self, config):
+        def put_to_unknown(definition):
+            pass # TODO
         self.on_new_menu_item_activate()
         for definition in config.get_definitions():
             name = definition.get_name()
             if name not in self.options:
-                pass # goes to unknown
+                put_to_unknown(definition)
+                continue
             check, field, var = self.options[name]
+            if (isinstance(definition, DefinitionElement) and var == 'list') 
or \
+                    (isinstance(definition, DefinitionTree) and var != 'list'):
+                put_to_unknown(definition)
+                continue
+            self.definitions[name] = definition
             check.set_active(True)
             if var == 'string':
                 field.set_text(definition.get_attribute('value'))
@@ -206,13 +219,9 @@ class PyGTKControl():
                 if definition.get_attribute('value').upper() != 'YES':
                     field.set_active(1)
             elif var == 'list':
-                if isinstance(definition, DefinitionElement):
+                for definition in definition.get_definitions():
                     field.get_model().append(
                         (definition.get_attribute('value'), ))
-                else:
-                    for definition in definition.get_definitions():
-                        field.get_model().append(
-                            (definition.get_attribute('value'), ))
 
     def construct_options(self):
         def make_tab_name(text):
@@ -235,29 +244,30 @@ class PyGTKControl():
             segregated_options[tab].append(option)
 
         tabs = {}
-        self.options = {}
+        self.options = {} # option name => (CheckButton, field, type, )
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             options = segregated_options.get(tab, [])
             tabs[tab] = gtk.Table(max(1, len(options)), 3)
             for i in xrange(len(options)):
                 option = options[i]
+                text, var = GUIConfig.options[option]
                 label = gtk.Label(make_name(option, tab))
-                label.set_tooltip_text(GUIConfig.options[option][0])
+                label.set_tooltip_text(text)
                 check = gtk.CheckButton()
-                if GUIConfig.options[option][1] == 'string':
+                if var == 'string':
                     field = gtk.Entry()
                     self.options[option] = (check, field, 'string', )
-                elif GUIConfig.options[option][1] == 'integer':
+                elif var == 'integer':
                     field = gtk.SpinButton(gtk.Adjustment(
                             0, 0, 2147483647, 1))
                     self.options[option] = (check, field, 'integer', )
-                elif GUIConfig.options[option][1] == 'bool':
+                elif var == 'bool':
                     field = gtk.combo_box_new_text()
                     field.append_text('yes')
                     field.append_text('no')
                     field.set_active(0)
                     self.options[option] = (check, field, 'bool', )
-                elif GUIConfig.options[option][1] == 'list':
+                elif var == 'list':
                     tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
                     tree.set_headers_visible(False)
                     tree_column = gtk.TreeViewColumn()
@@ -278,9 +288,9 @@ class PyGTKControl():
                         new_name.set_text('')
                     add_button.connect('clicked', add_to_list)
                     def remove_from_list(button):
-                        selected = tree.get_selection().get_selected()
-                        if selected[1] is not None:
-                            selected[0].remove(selected[1])
+                        model, selected = tree.get_selection().get_selected()
+                        if selected is not None:
+                            model.remove(selected)
                     remove_button.connect('clicked', remove_from_list)
                     self.options[option] = (check, tree, 'list', )
                 tabs[tab].attach(check, 0, 1, i, i + 1, gtk.FILL, gtk.FILL)
@@ -289,6 +299,8 @@ class PyGTKControl():
             self.widgets.get_widget('notebook').append_page(
                 tabs[tab], gtk.Label(make_tab_name(tab)))
 
+        self.definitions = {} # option name => corresponding Definition
+        self.on_new_menu_item_activate()
         self.widgets.get_widget('notebook').show_all()
 
 PyGTKControl()



commit 179a48e1d95df03a971357c1382463afe26c3960
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 16:13:55 2009 +0200

    Implemented getting/putting config on server.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index d93719a..e47a67b 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -21,9 +21,12 @@ import gobject
 import gtk.glade
 import GUIConfig
 from MyServer.pycontrollib.config import MyServerConfig
+from MyServer.pycontrollib.controller import Controller
 from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 
 class About():
+    '''GNU MyServer Control about window.'''
+
     def __init__(self):
         self.gladefile = 'PyGTKControl.glade'
         self.widgets = gtk.glade.XML(self.gladefile, 'aboutdialog')
@@ -32,24 +35,75 @@ class About():
     def on_aboutdialog_response(self, widget, response):
         widget.destroy()
 
+class Connection():
+    def __init__(self):
+        self.gladefile = 'PyGTKControl.glade'
+        self.widgets = gtk.glade.XML(self.gladefile, 'connectiondialog')
+        self.widgets.signal_autoconnect(self)
+
+    def destroy(self):
+        self.widgets.get_widget('connectiondialog').destroy()
+        
+    def on_cancel_button_clicked(self, widget):
+        self.destroy()
+    
+    def get_host(self):
+        return self.widgets.get_widget('host_entry').get_text()
+
+    def get_port(self):
+        return self.widgets.get_widget('port_entry').get_text()
+
+    def get_username(self):
+        return self.widgets.get_widget('username_entry').get_text()
+
+    def get_password(self):
+        return self.widgets.get_widget('password_entry').get_text()
+
 class PyGTKControl():
+    '''GNU MyServer Control main window.'''
+
     def __init__(self):
         self.gladefile = 'PyGTKControl.glade'
         self.widgets = gtk.glade.XML(self.gladefile, 'window')
         self.widgets.signal_autoconnect(self)
         self.construct_options()
-        self.chooser = None
-        self.path = None
+        self.chooser = None # Active file chooser
+        self.path = None # path of currently edited file
+        self.controller = None
 
     def on_window_destroy(self, widget):
         gtk.main_quit()
-        
+
     def on_quit_menu_item_activate(self, widget):
         gtk.main_quit()
 
     def on_about_menu_item_activate(self, widget):
         About()
 
+    def on_connect_menu_item_activate(self, widget):
+        dialog = Connection()
+        def connect(widget):
+            self.controller = Controller(
+                dialog.get_host(), dialog.get_port(),
+                dialog.get_username(), dialog.get_password())
+            self.controller.connect()
+            dialog.destroy()
+        dialog.widgets.get_widget('ok_button').connect('clicked', connect)
+        dialog.widgets.get_widget('connectiondialog').show()
+
+    def on_disconnect_menu_item_activate(self, widget):
+        if self.controller is not None:
+            self.controller.disconnect()
+        self.controller = None
+
+    def on_get_config_menu_item_activate(self, widget):
+        if self.controller is not None:
+            self.set_up(self.controller.get_server_configuration())
+
+    def on_put_config_menu_item_activate(self, widget):
+        if self.controller is not None:
+            self.controller.put_server_configuration(self.get_current_config())
+
     def on_open_menu_item_activate(self, widget):
         if self.chooser is not None:
             self.chooser.destroy()
@@ -62,7 +116,7 @@ class PyGTKControl():
                 self.path = self.chooser.get_filename()
                 with open(self.path) as f:
                     conf = MyServerConfig.from_string(f.read())
-                self.set_up(conf.get_definitions())
+                self.set_up(conf)
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
@@ -87,37 +141,40 @@ class PyGTKControl():
         if self.path is None:
             self.on_save_as_menu_item_activate(widget)
         else:
-            definitions = []
-            for option in self.options:
-                check, field, var = self.options[option]
-                if not check.get_active():
-                    continue
-                if var == 'string':
-                    definitions.append(
-                        DefinitionElement(option, {'value': field.get_text()}))
-                elif var == 'integer':
-                    value = str(int(field.get_value()))
-                    definitions.append(
-                        DefinitionElement(option, {'value': value}))
-                elif var == 'bool':
-                    value = 'YES' if field.get_active() else 'NO'
-                    definitions.append(
-                        DefinitionElement(option, {'value': value}))
-                elif var == 'list':
-                    values = []
-                    model = field.get_model()
-                    i = model.iter_children(None)
-                    while i is not None:
-                        values.append(model.get_value(i, 0))
-                        i = model.iter_next(i)
-                    values = map(
-                        lambda v: DefinitionElement(attributes = {'value': v}),
-                        values)
-                    definitions.append(
-                        DefinitionTree(option, values))
-            config = MyServerConfig(definitions)
+            config = self.get_current_config()
             with open(self.path, 'w') as f:
                 f.write(str(config))
+    
+    def get_current_config(self):
+        definitions = []
+        for option in self.options:
+            check, field, var = self.options[option]
+            if not check.get_active():
+                continue
+            if var == 'string':
+                definitions.append(
+                    DefinitionElement(option, {'value': field.get_text()}))
+            elif var == 'integer':
+                value = str(int(field.get_value()))
+                definitions.append(
+                    DefinitionElement(option, {'value': value}))
+            elif var == 'bool':
+                value = 'YES' if field.get_active() else 'NO'
+                definitions.append(
+                    DefinitionElement(option, {'value': value}))
+            elif var == 'list':
+                values = []
+                model = field.get_model()
+                i = model.iter_children(None)
+                while i is not None:
+                    values.append(model.get_value(i, 0))
+                    i = model.iter_next(i)
+                values = map(
+                    lambda v: DefinitionElement(attributes = {'value': v}),
+                    values)
+                definitions.append(
+                    DefinitionTree(option, values))
+        return MyServerConfig(definitions)
 
     def on_new_menu_item_activate(self, widget = None):
         if widget is not None:
@@ -133,9 +190,9 @@ class PyGTKControl():
             elif var == 'list':
                 field.get_model().clear()
 
-    def set_up(self, definitions):
+    def set_up(self, config):
         self.on_new_menu_item_activate()
-        for definition in definitions:
+        for definition in config.get_definitions():
             name = definition.get_name()
             if name not in self.options:
                 pass # goes to unknown
@@ -165,6 +222,7 @@ class PyGTKControl():
                 return text
             return text[len(tab):].replace('.', ' ').replace('_', ' ').strip()
 
+        # segregate options by tab
         segregated_options = {}
         for option in GUIConfig.options:
             tab = 'other'
@@ -175,7 +233,7 @@ class PyGTKControl():
             if not segregated_options.has_key(tab):
                 segregated_options[tab] = []
             segregated_options[tab].append(option)
-                
+
         tabs = {}
         self.options = {}
         for tab in GUIConfig.tabs + ['other', 'unknown']:
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index a2dc9be..b426812 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -75,6 +75,69 @@
               </widget>
             </child>
             <child>
+              <widget class="GtkMenuItem" id="remote_menu_item">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Remote</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="remote_menu">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="connect_menu_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_menu_item_activate"/>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="disconnect_menu_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_menu_item_activate"/>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" id="separator2">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="get_config_menu_item">
+                        <property name="label">Get configuration</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_get_config_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="get_config_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-go-down</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" 
id="put_config_menu_item">
+                        <property name="label">Put configuration</property>
+                        <property name="visible">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" 
handler="on_put_config_menu_item_activate"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="put_config_image">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-redo</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
               <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>
@@ -834,4 +897,163 @@ Public License instead of this License.  But first, 
please read
       </widget>
     </child>
   </widget>
+  <widget class="GtkDialog" id="connectiondialog">
+    <property name="border_width">5</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkTable" id="table">
+            <property name="visible">True</property>
+            <property name="n_rows">4</property>
+            <property name="n_columns">2</property>
+            <child>
+              <widget class="GtkLabel" id="host_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">host</property>
+              </widget>
+              <packing>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="port_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">port</property>
+              </widget>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="username_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">username</property>
+              </widget>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="password_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">password</property>
+              </widget>
+              <packing>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="host_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="port_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="username_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="password_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="on_cancel_button_clicked"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="ok_button">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="on_ok_button_clicked"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </glade-interface>



commit a12bca3717a6645005f19289d03431ed5d2c9cf0
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 16:06:19 2009 +0200

    Added methods to put configuration files back on server.

diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index 7eecdee..64cb7c6 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -131,3 +131,15 @@ class Controller(BasicController):
     def get_server_configuration(self):
         '''Get server settings.'''
         return MyServerConfig.from_string(self.get_file('myserver.xml'))
+
+    def put_MIME_type_configuration(self, config):
+        '''Put MIME types settings.'''
+        self.put_file(str(config), 'mimetypes.xml')
+
+    def put_vhost_configuration(self, config):
+        '''Put VHost settings.'''
+        self.put_file(str(config), 'virtualhosts.xml')
+
+    def put_server_configuration(self, config):
+        '''Put server settings.'''
+        self.put_file(str(config), 'myserver.xml')



commit d0d888d3379c57f14ee36216d6ba5c4c9a74c075
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 17 15:56:44 2009 +0200

    Auto change port to int.

diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index 847d93d..7eecdee 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -29,7 +29,7 @@ class BasicController():
     def __init__(self, host, port, username, password):
         self.connection = None
         self.host = host
-        self.port = port
+        self.port = int(port)
         self.username = username
         self.password = password
 



commit fabadfef11cb000eb1c9d42a21348fb89de40e24
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 21:50:15 2009 +0200

    Implemented save and save as.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 19d5896..d93719a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -39,6 +39,7 @@ class PyGTKControl():
         self.widgets.signal_autoconnect(self)
         self.construct_options()
         self.chooser = None
+        self.path = None
 
     def on_window_destroy(self, widget):
         gtk.main_quit()
@@ -58,15 +59,69 @@ class PyGTKControl():
                        gtk.STOCK_OPEN, gtk.RESPONSE_OK))
         def handle_response(widget, response):
             if response == gtk.RESPONSE_OK:
-                path = self.chooser.get_filename()
-                with open(path) as f:
+                self.path = self.chooser.get_filename()
+                with open(self.path) as f:
                     conf = MyServerConfig.from_string(f.read())
                 self.set_up(conf.get_definitions())
             self.chooser.destroy()
         self.chooser.connect('response', handle_response)
         self.chooser.show()
 
+    def on_save_as_menu_item_activate(self, widget):
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Save configuration file.',
+            action = gtk.FILE_CHOOSER_ACTION_SAVE,
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_SAVE_AS, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                self.path = self.chooser.get_filename()
+                self.on_save_menu_item_activate(widget)
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
+    def on_save_menu_item_activate(self, widget):
+        if self.path is None:
+            self.on_save_as_menu_item_activate(widget)
+        else:
+            definitions = []
+            for option in self.options:
+                check, field, var = self.options[option]
+                if not check.get_active():
+                    continue
+                if var == 'string':
+                    definitions.append(
+                        DefinitionElement(option, {'value': field.get_text()}))
+                elif var == 'integer':
+                    value = str(int(field.get_value()))
+                    definitions.append(
+                        DefinitionElement(option, {'value': value}))
+                elif var == 'bool':
+                    value = 'YES' if field.get_active() else 'NO'
+                    definitions.append(
+                        DefinitionElement(option, {'value': value}))
+                elif var == 'list':
+                    values = []
+                    model = field.get_model()
+                    i = model.iter_children(None)
+                    while i is not None:
+                        values.append(model.get_value(i, 0))
+                        i = model.iter_next(i)
+                    values = map(
+                        lambda v: DefinitionElement(attributes = {'value': v}),
+                        values)
+                    definitions.append(
+                        DefinitionTree(option, values))
+            config = MyServerConfig(definitions)
+            with open(self.path, 'w') as f:
+                f.write(str(config))
+
     def on_new_menu_item_activate(self, widget = None):
+        if widget is not None:
+            self.path = None
         for check, field, var in self.options.itervalues():
             check.set_active(False)
             if var == 'string':



commit a11d241ee8b86aa95f309bcd97f3b17450f9028c
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 21:04:57 2009 +0200

    Removed combobox option.

diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
index 1036e63..60edc00 100644
--- a/misc/PyGTK_Control/GUIConfig.py
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -38,7 +38,7 @@ options['http.home_directory'] = ('Define name for home 
directory browsing.', 's
 options['http.default_file'] = ('Default filename to send in a directory if 
the file isn\'t in the path then the directory content is sent.', 'list', )
 
 # Server
-options['server.language'] = ('Choose the language to use for the server.', 
'combobox', )
+options['server.language'] = ('Choose the language to use for the server.', 
'string', )
 options['server.verbosity'] = ('Verbosity on log file.', 'integer', )
 options['server.static_threads'] = ('Number of serving threads always 
active.', 'integer', )
 options['server.max_threads'] = ('Maximum number of serving threads that the 
scheduler can create.', 'integer', )
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index ff4e689..19d5896 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -77,8 +77,6 @@ class PyGTKControl():
                 field.set_active(0)
             elif var == 'list':
                 field.get_model().clear()
-            elif var == 'combobox':
-                field.get_model().clear()
 
     def set_up(self, definitions):
         self.on_new_menu_item_activate()
@@ -103,8 +101,6 @@ class PyGTKControl():
                     for definition in definition.get_definitions():
                         field.get_model().append(
                             (definition.get_attribute('value'), ))
-            elif var == 'combobox':
-                pass # TODO
 
     def construct_options(self):
         def make_tab_name(text):
@@ -148,9 +144,6 @@ class PyGTKControl():
                     field.append_text('no')
                     field.set_active(0)
                     self.options[option] = (check, field, 'bool', )
-                elif GUIConfig.options[option][1] == 'combobox':
-                    field = gtk.combo_box_new_text()
-                    self.options[option] = (check, field, 'combobox', )
                 elif GUIConfig.options[option][1] == 'list':
                     tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
                     tree.set_headers_visible(False)



commit 1d19d2a6c91aafc7c351524f55138ebe760bc30c
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 20:53:23 2009 +0200

    File opening in MyServercontrol.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 2fbcc5d..ff4e689 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -20,6 +20,8 @@ import gtk
 import gobject
 import gtk.glade
 import GUIConfig
+from MyServer.pycontrollib.config import MyServerConfig
+from MyServer.pycontrollib.definition import DefinitionElement, DefinitionTree
 
 class About():
     def __init__(self):
@@ -36,6 +38,7 @@ class PyGTKControl():
         self.widgets = gtk.glade.XML(self.gladefile, 'window')
         self.widgets.signal_autoconnect(self)
         self.construct_options()
+        self.chooser = None
 
     def on_window_destroy(self, widget):
         gtk.main_quit()
@@ -46,7 +49,24 @@ class PyGTKControl():
     def on_about_menu_item_activate(self, widget):
         About()
 
-    def on_new_menu_item_activate(self, widget):
+    def on_open_menu_item_activate(self, widget):
+        if self.chooser is not None:
+            self.chooser.destroy()
+        self.chooser = gtk.FileChooserDialog(
+            'Open configuration file.',
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        def handle_response(widget, response):
+            if response == gtk.RESPONSE_OK:
+                path = self.chooser.get_filename()
+                with open(path) as f:
+                    conf = MyServerConfig.from_string(f.read())
+                self.set_up(conf.get_definitions())
+            self.chooser.destroy()
+        self.chooser.connect('response', handle_response)
+        self.chooser.show()
+
+    def on_new_menu_item_activate(self, widget = None):
         for check, field, var in self.options.itervalues():
             check.set_active(False)
             if var == 'string':
@@ -60,6 +80,32 @@ class PyGTKControl():
             elif var == 'combobox':
                 field.get_model().clear()
 
+    def set_up(self, definitions):
+        self.on_new_menu_item_activate()
+        for definition in definitions:
+            name = definition.get_name()
+            if name not in self.options:
+                pass # goes to unknown
+            check, field, var = self.options[name]
+            check.set_active(True)
+            if var == 'string':
+                field.set_text(definition.get_attribute('value'))
+            elif var == 'integer':
+                field.set_value(int(definition.get_attribute('value')))
+            elif var == 'bool':
+                if definition.get_attribute('value').upper() != 'YES':
+                    field.set_active(1)
+            elif var == 'list':
+                if isinstance(definition, DefinitionElement):
+                    field.get_model().append(
+                        (definition.get_attribute('value'), ))
+                else:
+                    for definition in definition.get_definitions():
+                        field.get_model().append(
+                            (definition.get_attribute('value'), ))
+            elif var == 'combobox':
+                pass # TODO
+
     def construct_options(self):
         def make_tab_name(text):
             return text.capitalize().replace('.', ' ').replace('_', ' ')
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index e531e07..a2dc9be 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -35,6 +35,7 @@
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
+                        <signal name="activate" 
handler="on_open_menu_item_activate"/>
                       </widget>
                     </child>
                     <child>
@@ -43,6 +44,7 @@
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
+                        <signal name="activate" 
handler="on_save_menu_item_activate"/>
                       </widget>
                     </child>
                     <child>
@@ -51,6 +53,7 @@
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
+                        <signal name="activate" 
handler="on_save_as_menu_item_activate"/>
                       </widget>
                     </child>
                     <child>



commit 790c62ddd0e69e123cf9e9096e1e3da239c2ad51
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 20:10:38 2009 +0200

    Accept lower case "yes".

diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index c9dbaf3..eeed6da 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -96,7 +96,7 @@ class Stream():
         cycle = root.get('cycle', None)
         cycle_gzip = root.get('cycle_gzip', None)
         if cycle_gzip is not None:
-            cycle_gzip = True if cycle_gzip == 'YES' else False
+            cycle_gzip = cycle_gzip.upper() == 'YES'
         filters = []
         for child in list(root):
             if child.tag == 'FILTER':
diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 1f0bdde..43a13cb 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -180,7 +180,7 @@ class MIMEType():
         param = root.get('param', None)
         self_executed = root.get('self', None)
         if self_executed is not None:
-            self_executed = self_executed == 'YES'
+            self_executed = self_executed.upper() == 'YES'
         path = None
         extension = set()
         filters = []
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 4609774..d1bd76a 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -222,7 +222,7 @@ class VHost():
             elif child.tag == 'HOST':
                 use_regex = child.get('useRegex', None)
                 if use_regex is not None:
-                    use_regex = use_regex == 'YES'
+                    use_regex = use_regex.upper() == 'YES'
                 host[child.text] = use_regex
             elif child.tag == 'SSL_PRIVATEKEY':
                 private_key = child.text



commit c993a0924b9857894396d3c5a08340bba10f7725
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 17:10:51 2009 +0200

    Implemented menu->new

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 54f54a1..2fbcc5d 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -46,6 +46,20 @@ class PyGTKControl():
     def on_about_menu_item_activate(self, widget):
         About()
 
+    def on_new_menu_item_activate(self, widget):
+        for check, field, var in self.options.itervalues():
+            check.set_active(False)
+            if var == 'string':
+                field.set_text('')
+            elif var == 'integer':
+                field.set_value(0)
+            elif var == 'bool':
+                field.set_active(0)
+            elif var == 'list':
+                field.get_model().clear()
+            elif var == 'combobox':
+                field.get_model().clear()
+
     def construct_options(self):
         def make_tab_name(text):
             return text.capitalize().replace('.', ' ').replace('_', ' ')
@@ -66,6 +80,7 @@ class PyGTKControl():
             segregated_options[tab].append(option)
                 
         tabs = {}
+        self.options = {}
         for tab in GUIConfig.tabs + ['other', 'unknown']:
             options = segregated_options.get(tab, [])
             tabs[tab] = gtk.Table(max(1, len(options)), 3)
@@ -76,15 +91,20 @@ class PyGTKControl():
                 check = gtk.CheckButton()
                 if GUIConfig.options[option][1] == 'string':
                     field = gtk.Entry()
+                    self.options[option] = (check, field, 'string', )
                 elif GUIConfig.options[option][1] == 'integer':
-                    field = gtk.SpinButton()
+                    field = gtk.SpinButton(gtk.Adjustment(
+                            0, 0, 2147483647, 1))
+                    self.options[option] = (check, field, 'integer', )
                 elif GUIConfig.options[option][1] == 'bool':
                     field = gtk.combo_box_new_text()
                     field.append_text('yes')
                     field.append_text('no')
                     field.set_active(0)
+                    self.options[option] = (check, field, 'bool', )
                 elif GUIConfig.options[option][1] == 'combobox':
                     field = gtk.combo_box_new_text()
+                    self.options[option] = (check, field, 'combobox', )
                 elif GUIConfig.options[option][1] == 'list':
                     tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
                     tree.set_headers_visible(False)
@@ -110,6 +130,7 @@ class PyGTKControl():
                         if selected[1] is not None:
                             selected[0].remove(selected[1])
                     remove_button.connect('clicked', remove_from_list)
+                    self.options[option] = (check, tree, 'list', )
                 tabs[tab].attach(check, 0, 1, i, i + 1, gtk.FILL, gtk.FILL)
                 tabs[tab].attach(label, 1, 2, i, i + 1, yoptions = gtk.FILL)
                 tabs[tab].attach(field, 2, 3, i, i + 1, yoptions = gtk.FILL)
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 6c3dcb4..e531e07 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -26,6 +26,7 @@
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
+                        <signal name="activate" 
handler="on_new_menu_item_activate"/>
                       </widget>
                     </child>
                     <child>



commit be87aa94c3a48fe25109f269ecc8dfaf82b68397
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 16:01:41 2009 +0200

    Added about window.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index c78242c..54f54a1 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -21,6 +21,15 @@ import gobject
 import gtk.glade
 import GUIConfig
 
+class About():
+    def __init__(self):
+        self.gladefile = 'PyGTKControl.glade'
+        self.widgets = gtk.glade.XML(self.gladefile, 'aboutdialog')
+        self.widgets.signal_autoconnect(self)
+
+    def on_aboutdialog_response(self, widget, response):
+        widget.destroy()
+
 class PyGTKControl():
     def __init__(self):
         self.gladefile = 'PyGTKControl.glade'
@@ -30,6 +39,12 @@ class PyGTKControl():
 
     def on_window_destroy(self, widget):
         gtk.main_quit()
+        
+    def on_quit_menu_item_activate(self, widget):
+        gtk.main_quit()
+
+    def on_about_menu_item_activate(self, widget):
+        About()
 
     def construct_options(self):
         def make_tab_name(text):
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 80adba3..6c3dcb4 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -10,18 +10,18 @@
         <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <child>
-          <widget class="GtkMenuBar" id="menubar1">
+          <widget class="GtkMenuBar" id="menubar">
             <property name="visible">True</property>
             <child>
-              <widget class="GtkMenuItem" id="menuitem1">
+              <widget class="GtkMenuItem" id="file_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_File</property>
                 <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkMenu" id="menu1">
+                  <widget class="GtkMenu" id="file_menu">
                     <property name="visible">True</property>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem1">
+                      <widget class="GtkImageMenuItem" id="new_menu_item">
                         <property name="label">gtk-new</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -29,7 +29,7 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem2">
+                      <widget class="GtkImageMenuItem" id="open_menu_item">
                         <property name="label">gtk-open</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -37,7 +37,7 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem3">
+                      <widget class="GtkImageMenuItem" id="save_menu_item">
                         <property name="label">gtk-save</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -45,7 +45,7 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem4">
+                      <widget class="GtkImageMenuItem" id="save_as_menu_item">
                         <property name="label">gtk-save-as</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -53,16 +53,17 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkSeparatorMenuItem" 
id="separatormenuitem1">
+                      <widget class="GtkSeparatorMenuItem" id="separator1">
                         <property name="visible">True</property>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem5">
+                      <widget class="GtkImageMenuItem" id="quit_menu_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_menu_item_activate"/>
                       </widget>
                     </child>
                   </widget>
@@ -70,70 +71,20 @@
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="menuitem2">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Edit</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu2">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem6">
-                        <property name="label">gtk-cut</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem7">
-                        <property name="label">gtk-copy</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem8">
-                        <property name="label">gtk-paste</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem9">
-                        <property name="label">gtk-delete</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem3">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_View</property>
-                <property name="use_underline">True</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem4">
+              <widget class="GtkMenuItem" id="help_menu_item">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">_Help</property>
                 <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkMenu" id="menu3">
+                  <widget class="GtkMenu" id="help_menu">
                     <property name="visible">True</property>
                     <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem10">
+                      <widget class="GtkImageMenuItem" id="about_menu_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_menu_item_activate"/>
                       </widget>
                     </child>
                   </widget>
@@ -168,4 +119,715 @@
       </widget>
     </child>
   </widget>
+  <widget class="GtkAboutDialog" id="aboutdialog">
+    <property name="visible">True</property>
+    <property name="border_width">5</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <property name="program_name">GNU MyServer Control</property>
+    <property name="version">v0.0</property>
+    <property name="copyright" translatable="yes">Copyright (C) 2009 Free 
Software Foundation, Inc.
+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.</property>
+    <property name="website">http://www.gnu.org/software/myserver/</property>
+    <property name="license">                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. &lt;http://fsf.org/&gt;
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    &lt;one line to give the program's name and a brief idea of what it 
does.&gt;
+    Copyright (C) &lt;year&gt;  &lt;name of author&gt;
+
+    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 &lt;http://www.gnu.org/licenses/&gt;.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    &lt;program&gt;  Copyright (C) &lt;year&gt;  &lt;name of author&gt;
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+&lt;http://www.gnu.org/licenses/&gt;.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+&lt;http://www.gnu.org/philosophy/why-not-lgpl.html&gt;.
+</property>
+    <signal name="response" handler="on_aboutdialog_response"/>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <placeholder/>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </glade-interface>



commit d79ec32935886b1b4864ae217975e81e12268d01
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 12:16:32 2009 +0200

    Added copyright header.

diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
index f4600e2..1036e63 100644
--- a/misc/PyGTK_Control/GUIConfig.py
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -1,3 +1,21 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
 options = {}
 
 # Control



commit d603a00a9f1529278b7cd92754fd06bb8603c98f
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 16 12:11:17 2009 +0200

    List is fully functional.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 83bf74a..c78242c 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -72,6 +72,12 @@ class PyGTKControl():
                     field = gtk.combo_box_new_text()
                 elif GUIConfig.options[option][1] == 'list':
                     tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
+                    tree.set_headers_visible(False)
+                    tree_column = gtk.TreeViewColumn()
+                    tree_renderer = gtk.CellRendererText()
+                    tree_column.pack_start(tree_renderer)
+                    tree_column.add_attribute(tree_renderer, 'text', 0)
+                    tree.append_column(tree_column)
                     new_name = gtk.Entry()
                     add_button = gtk.Button('add')
                     remove_button = gtk.Button('remove')
@@ -80,6 +86,15 @@ class PyGTKControl():
                     field.attach(add_button, 0, 1, 1, 2)
                     field.attach(remove_button, 1, 2, 1, 2)
                     field.attach(tree, 0, 2, 2, 3)
+                    def add_to_list(button):
+                        tree.get_model().append((new_name.get_text(), ))
+                        new_name.set_text('')
+                    add_button.connect('clicked', add_to_list)
+                    def remove_from_list(button):
+                        selected = tree.get_selection().get_selected()
+                        if selected[1] is not None:
+                            selected[0].remove(selected[1])
+                    remove_button.connect('clicked', remove_from_list)
                 tabs[tab].attach(check, 0, 1, i, i + 1, gtk.FILL, gtk.FILL)
                 tabs[tab].attach(label, 1, 2, i, i + 1, yoptions = gtk.FILL)
                 tabs[tab].attach(field, 2, 3, i, i + 1, yoptions = gtk.FILL)



commit c7bfa02a092d4d79e413a550772cbb5eea4c891a
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 15 23:34:32 2009 +0200

    Changed GUI to dynamic one.
    
    Now GUI creates all options on start.

diff --git a/misc/PyGTK_Control/GUIConfig.py b/misc/PyGTK_Control/GUIConfig.py
new file mode 100644
index 0000000..f4600e2
--- /dev/null
+++ b/misc/PyGTK_Control/GUIConfig.py
@@ -0,0 +1,51 @@
+options = {}
+
+# Control
+options['control.enabled'] = ('Set this to yes if you want to enable control 
protocol.', 'bool', )
+options['control.admin'] = ('Set here the name for the control admin.', 
'string', )
+options['control.password'] = ('Define here a good, strong password for the 
admin.', 'string', )
+
+# FTP
+options['ftp.allow_anonymous'] = ('Allow anonymous login.', 'bool', )
+options['ftp.anonymous_need_pass'] = ('If anonymous allowed, tells if needs 
pass.', 'bool', )
+options['ftp.allow_asynchronous_cmds'] = ('Allow asynchronous cmds like Abor, 
Quit, Stat.', 'bool', )
+options['ftp.allow_pipelining'] = ('Allow clients to send more than one 
command to server in one request.', 'bool', )
+options['ftp.allow_store'] = ('Allow clients to change server files.', 'bool', 
)
+
+# HTTP
+options['http.use_error_file'] = ('Set this to yes to use personalized error 
pages from system directory.', 'bool', )
+options['http.dir.css'] = ('Define the folder browsing style.', 'string', )
+options['http.use_home_directory'] = ('Enable home directory per user', 
'bool', )
+options['http.home_directory'] = ('Define name for home directory browsing.', 
'string', )
+options['http.default_file'] = ('Default filename to send in a directory if 
the file isn\'t in the path then the directory content is sent.', 'list', )
+
+# Server
+options['server.language'] = ('Choose the language to use for the server.', 
'combobox', )
+options['server.verbosity'] = ('Verbosity on log file.', 'integer', )
+options['server.static_threads'] = ('Number of serving threads always 
active.', 'integer', )
+options['server.max_threads'] = ('Maximum number of serving threads that the 
scheduler can create.', 'integer', )
+options['server.buffer_size'] = ('Dimension of every buffer in bytes.', 
'integer', )
+options['server.max_connections'] = ('Define the max number of connections to 
allow to server. 0 means allow infinite connections.', 'integer', )
+options['server.max_accepted_connections'] = ('Define max number of 
connections to accept.', 'integer', )
+options['server.max_log_size'] = ('max size of the log file in bytes.', 
'integer', )
+options['server.admin'] = ('Administrator e-mail.', 'string', )
+options['server.max_files_cache'] = ('Max cache size for static files.', 
'integer', )
+options['server.max_file_cache'] = ('Max cache size for single static file.', 
'integer', )
+options['server.min_file_cache'] = ('Min cache size for single static file.', 
'integer', )
+options['server.max_servers'] = ('Maximum number of external servers that can 
be executed.', 'integer', )
+
+# Log color
+options['log_color.info_fg'] = ('Info log foreground colour.', 'string', )
+options['log_color.info_bg'] = ('Info log background colour.', 'string', )
+options['log_color.warning_fg'] = ('Warning log foreground colour.', 'string', 
)
+options['log_color.warning_bg'] = ('Warning log background colour.', 'string', 
)
+options['log_color.error_fg'] = ('Error log foreground colour.', 'string', )
+options['log_color.error_bg'] = ('Error log background colour.', 'string', )
+
+# Other
+options['connection.timeout'] = ('Timeout for every client\'s connected to 
server. If the client doesn\'t request anything for n seconds the connection 
with the client is closed. Set this to 0 if you don\'t want to use 
keep-alive-connections', 'integer', )
+options['gzip.threshold'] = ('Define the gzip compression threshold value.', 
'integer', )
+options['symlinks.follow'] = ('Define if links should be followed.', 'bool', )
+
+# don't put 'other' or 'unknown' here
+tabs = ['server', 'control', 'ftp', 'http', 'log_color']
diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
index 0b497cb..83bf74a 100644
--- a/misc/PyGTK_Control/MyServerControl.py
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -17,16 +17,76 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 import gtk
+import gobject
 import gtk.glade
+import GUIConfig
 
 class PyGTKControl():
     def __init__(self):
         self.gladefile = 'PyGTKControl.glade'
         self.widgets = gtk.glade.XML(self.gladefile, 'window')
         self.widgets.signal_autoconnect(self)
+        self.construct_options()
 
     def on_window_destroy(self, widget):
         gtk.main_quit()
 
+    def construct_options(self):
+        def make_tab_name(text):
+            return text.capitalize().replace('.', ' ').replace('_', ' ')
+        def make_name(text, tab):
+            if tab == 'other':
+                return text
+            return text[len(tab):].replace('.', ' ').replace('_', ' ').strip()
+
+        segregated_options = {}
+        for option in GUIConfig.options:
+            tab = 'other'
+            for prefix in GUIConfig.tabs:
+                if option.startswith(prefix):
+                    tab = prefix
+                    break
+            if not segregated_options.has_key(tab):
+                segregated_options[tab] = []
+            segregated_options[tab].append(option)
+                
+        tabs = {}
+        for tab in GUIConfig.tabs + ['other', 'unknown']:
+            options = segregated_options.get(tab, [])
+            tabs[tab] = gtk.Table(max(1, len(options)), 3)
+            for i in xrange(len(options)):
+                option = options[i]
+                label = gtk.Label(make_name(option, tab))
+                label.set_tooltip_text(GUIConfig.options[option][0])
+                check = gtk.CheckButton()
+                if GUIConfig.options[option][1] == 'string':
+                    field = gtk.Entry()
+                elif GUIConfig.options[option][1] == 'integer':
+                    field = gtk.SpinButton()
+                elif GUIConfig.options[option][1] == 'bool':
+                    field = gtk.combo_box_new_text()
+                    field.append_text('yes')
+                    field.append_text('no')
+                    field.set_active(0)
+                elif GUIConfig.options[option][1] == 'combobox':
+                    field = gtk.combo_box_new_text()
+                elif GUIConfig.options[option][1] == 'list':
+                    tree  = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
+                    new_name = gtk.Entry()
+                    add_button = gtk.Button('add')
+                    remove_button = gtk.Button('remove')
+                    field = gtk.Table(3, 2)
+                    field.attach(new_name, 0, 2, 0, 1)
+                    field.attach(add_button, 0, 1, 1, 2)
+                    field.attach(remove_button, 1, 2, 1, 2)
+                    field.attach(tree, 0, 2, 2, 3)
+                tabs[tab].attach(check, 0, 1, i, i + 1, gtk.FILL, gtk.FILL)
+                tabs[tab].attach(label, 1, 2, i, i + 1, yoptions = gtk.FILL)
+                tabs[tab].attach(field, 2, 3, i, i + 1, yoptions = gtk.FILL)
+            self.widgets.get_widget('notebook').append_page(
+                tabs[tab], gtk.Label(make_tab_name(tab)))
+
+        self.widgets.get_widget('notebook').show_all()
+
 PyGTKControl()
 gtk.main()
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
index 9492698..80adba3 100644
--- a/misc/PyGTK_Control/PyGTKControl.glade
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -150,1298 +150,6 @@
           <widget class="GtkNotebook" id="notebook">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <child>
-              <widget class="GtkTable" id="control_table">
-                <property name="visible">True</property>
-                <property name="n_rows">3</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <widget class="GtkLabel" id="control_enabled_label">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">enabled</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="control_enabled_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="control_admin_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">admin</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="control_admin_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="control_password_label">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">password</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="control_password_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="control_enabled_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="control_admin_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="control_password_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="control_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Control</property>
-              </widget>
-              <packing>
-                <property name="tab_fill">False</property>
-                <property name="type">tab</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="ftp_table">
-                <property name="visible">True</property>
-                <property name="n_rows">5</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <widget class="GtkLabel" id="ftp_allow_anonymous_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">allow 
anonymous</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" 
id="ftp_allow_anonymous_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="ftp_anonymous_need_pass_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">anonymous need 
pass</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" 
id="ftp_anonymous_need_pass_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="ftp_allow_asynchronous_cmds">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">allow 
asynchronous cmds</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" 
id="ftp_allow_asynchronous_cmds_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="ftp_allow_pipelining_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">allow 
pipelining</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" 
id="ftp_allow_pipelining_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="ftp_allow_store_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">allow 
store</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="ftp_allow_store_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="ftp_allow_anonymous_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="ftp_anonymous_need_pass_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="ftp_allow_asynchronous_cmds_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="ftp_allow_pipelining_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="ftp_allow_store_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="ftp_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">FTP</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-                <property name="tab_fill">False</property>
-                <property name="type">tab</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="http_table">
-                <property name="visible">True</property>
-                <property name="n_rows">8</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <widget class="GtkCheckButton" 
id="http_use_error_file_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="http_use_error_file_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">use error 
file</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" 
id="http_use_error_file_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="http_use_home_directory_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="http_home_directory_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="http_use_home_directory_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">use home 
directory</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="use_home_directory_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="http_home_directory_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">home 
directory</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="http_home_directory_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkTreeView" id="http_default_file_treeview">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                  </widget>
-                  <packing>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">8</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="http_default_file_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkButton" id="http_default_file_add_button">
-                    <property name="label" translatable="yes">add default 
file</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkButton" 
id="http_default_file_remove_button">
-                    <property name="label" translatable="yes">remove selected 
default files</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="http_fill_label_1">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">7</property>
-                    <property name="bottom_attach">8</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="http_dir_css_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="http_dir_css_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">http dir 
css</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="http_dir_css_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="http_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">HTTP</property>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-                <property name="tab_fill">False</property>
-                <property name="type">tab</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="server_table">
-                <property name="visible">True</property>
-                <property name="n_rows">13</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_language_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_language_label">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">language</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="server_language_combobox">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_verbosity_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_verbosity_label">
-                    <property name="visible">True</property>
-                    <property name="label" 
translatable="yes">verbosity</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_verbosity_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_static_threads_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_static_threads_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">static 
threads</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_static_threads_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_threads_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_threads_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max 
threads</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_threads_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_buffer_size_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_buffer_size_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">buffer 
size</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_buffer_size_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_connections_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_connections_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max 
connections</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_connections_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_accepted_connections_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" 
id="server_max_accepted_connections_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max accepted 
connections</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_accepted_connections_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="server_admin_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">7</property>
-                    <property name="bottom_attach">8</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_admin_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">admin</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">7</property>
-                    <property name="bottom_attach">8</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="server_admin_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">7</property>
-                    <property name="bottom_attach">8</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_files_cache_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">8</property>
-                    <property name="bottom_attach">9</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_files_cache_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max files 
cache</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">8</property>
-                    <property name="bottom_attach">9</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_files_cache_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">8</property>
-                    <property name="bottom_attach">9</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_file_cache_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">9</property>
-                    <property name="bottom_attach">10</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_file_cache_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max file 
cache</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">9</property>
-                    <property name="bottom_attach">10</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_file_cache_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">9</property>
-                    <property name="bottom_attach">10</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_min_file_cache_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">10</property>
-                    <property name="bottom_attach">11</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_min_file_cache_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">min file 
cache</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">10</property>
-                    <property name="bottom_attach">11</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_min_file_cache_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">10</property>
-                    <property name="bottom_attach">11</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_servers_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">11</property>
-                    <property name="bottom_attach">12</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_servers_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max 
servers</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">11</property>
-                    <property name="bottom_attach">12</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_servers_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">11</property>
-                    <property name="bottom_attach">12</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="server_max_log_size_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">12</property>
-                    <property name="bottom_attach">13</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="server_max_log_size_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">max log 
size</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">12</property>
-                    <property name="bottom_attach">13</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="server_max_log_size_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">12</property>
-                    <property name="bottom_attach">13</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="server_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Server</property>
-              </widget>
-              <packing>
-                <property name="position">3</property>
-                <property name="tab_fill">False</property>
-                <property name="type">tab</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="other_table">
-                <property name="visible">True</property>
-                <property name="n_rows">3</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <widget class="GtkCheckButton" 
id="connection_timeout_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="connection_timeout_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">connection 
timeout</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" 
id="connection_timeout_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="gzip_threshold_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="gzip_threshold_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">gzip 
threshold</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSpinButton" id="gzip_threshold_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" 
id="symlinks_follow_checkbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="symlinks_follow_label">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">symlinks 
follow</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="symlinks_follow_combobox">
-                    <property name="visible">True</property>
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">yes
-no</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="other_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Other</property>
-              </widget>
-              <packing>
-                <property name="position">4</property>
-                <property name="tab_fill">False</property>
-                <property name="type">tab</property>
-              </packing>
-            </child>
           </widget>
           <packing>
             <property name="position">1</property>



commit fbb478a19413cc0db6222da7bd5f19c75916785b
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 13 22:47:26 2009 +0200

    First GUI design.

diff --git a/misc/PyGTK_Control/MyServerControl.py 
b/misc/PyGTK_Control/MyServerControl.py
new file mode 100644
index 0000000..0b497cb
--- /dev/null
+++ b/misc/PyGTK_Control/MyServerControl.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 gtk
+import gtk.glade
+
+class PyGTKControl():
+    def __init__(self):
+        self.gladefile = 'PyGTKControl.glade'
+        self.widgets = gtk.glade.XML(self.gladefile, 'window')
+        self.widgets.signal_autoconnect(self)
+
+    def on_window_destroy(self, widget):
+        gtk.main_quit()
+
+PyGTKControl()
+gtk.main()
diff --git a/misc/PyGTK_Control/PyGTKControl.glade 
b/misc/PyGTK_Control/PyGTKControl.glade
new file mode 100644
index 0000000..9492698
--- /dev/null
+++ b/misc/PyGTK_Control/PyGTKControl.glade
@@ -0,0 +1,1463 @@
+<?xml version="1.0"?>
+<glade-interface>
+  <!-- interface-requires gtk+ 2.16 -->
+  <!-- interface-naming-policy toplevel-contextual -->
+  <widget class="GtkWindow" id="window">
+    <property name="visible">True</property>
+    <signal name="destroy" handler="on_window_destroy"/>
+    <child>
+      <widget class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <widget class="GtkMenuBar" id="menubar1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_File</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu1">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem1">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem2">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem3">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem4">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" 
id="separatormenuitem1">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem5">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Edit</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu2">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem6">
+                        <property name="label">gtk-cut</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem8">
+                        <property name="label">gtk-paste</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem9">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem3">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_View</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Help</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu3">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem10">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkNotebook" id="notebook">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <widget class="GtkTable" id="control_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <widget class="GtkLabel" id="control_enabled_label">
+                    <property name="visible">True</property>
+                    <property name="label" 
translatable="yes">enabled</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="control_enabled_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="control_admin_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">admin</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="control_admin_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="control_password_label">
+                    <property name="visible">True</property>
+                    <property name="label" 
translatable="yes">password</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="control_password_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="control_enabled_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="control_admin_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="control_password_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="control_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Control</property>
+              </widget>
+              <packing>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="ftp_table">
+                <property name="visible">True</property>
+                <property name="n_rows">5</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <widget class="GtkLabel" id="ftp_allow_anonymous_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">allow 
anonymous</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" 
id="ftp_allow_anonymous_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="ftp_anonymous_need_pass_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">anonymous need 
pass</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" 
id="ftp_anonymous_need_pass_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="ftp_allow_asynchronous_cmds">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">allow 
asynchronous cmds</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" 
id="ftp_allow_asynchronous_cmds_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="ftp_allow_pipelining_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">allow 
pipelining</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" 
id="ftp_allow_pipelining_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="ftp_allow_store_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">allow 
store</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="ftp_allow_store_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="ftp_allow_anonymous_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="ftp_anonymous_need_pass_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="ftp_allow_asynchronous_cmds_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="ftp_allow_pipelining_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="ftp_allow_store_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="ftp_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">FTP</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="http_table">
+                <property name="visible">True</property>
+                <property name="n_rows">8</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <widget class="GtkCheckButton" 
id="http_use_error_file_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="http_use_error_file_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">use error 
file</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" 
id="http_use_error_file_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="http_use_home_directory_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="http_home_directory_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="http_use_home_directory_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">use home 
directory</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="use_home_directory_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="http_home_directory_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">home 
directory</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="http_home_directory_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkTreeView" id="http_default_file_treeview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </widget>
+                  <packing>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">8</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="http_default_file_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">5</property>
+                    <property name="bottom_attach">6</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="http_default_file_add_button">
+                    <property name="label" translatable="yes">add default 
file</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" 
id="http_default_file_remove_button">
+                    <property name="label" translatable="yes">remove selected 
default files</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="http_fill_label_1">
+                    <property name="visible">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="http_dir_css_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="http_dir_css_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">http dir 
css</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="http_dir_css_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="http_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">HTTP</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="server_table">
+                <property name="visible">True</property>
+                <property name="n_rows">13</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_language_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_language_label">
+                    <property name="visible">True</property>
+                    <property name="label" 
translatable="yes">language</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="server_language_combobox">
+                    <property name="visible">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_verbosity_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_verbosity_label">
+                    <property name="visible">True</property>
+                    <property name="label" 
translatable="yes">verbosity</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_verbosity_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_static_threads_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_static_threads_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">static 
threads</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_static_threads_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_threads_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_threads_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max 
threads</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_threads_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_buffer_size_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_buffer_size_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">buffer 
size</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_buffer_size_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_connections_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">5</property>
+                    <property name="bottom_attach">6</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_connections_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max 
connections</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">5</property>
+                    <property name="bottom_attach">6</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_connections_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">5</property>
+                    <property name="bottom_attach">6</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_accepted_connections_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" 
id="server_max_accepted_connections_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max accepted 
connections</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_accepted_connections_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="server_admin_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_admin_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">admin</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="server_admin_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_files_cache_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">8</property>
+                    <property name="bottom_attach">9</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_files_cache_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max files 
cache</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">8</property>
+                    <property name="bottom_attach">9</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_files_cache_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">8</property>
+                    <property name="bottom_attach">9</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_file_cache_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">9</property>
+                    <property name="bottom_attach">10</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_file_cache_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max file 
cache</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">9</property>
+                    <property name="bottom_attach">10</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_file_cache_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">9</property>
+                    <property name="bottom_attach">10</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_min_file_cache_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">10</property>
+                    <property name="bottom_attach">11</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_min_file_cache_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">min file 
cache</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">10</property>
+                    <property name="bottom_attach">11</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_min_file_cache_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">10</property>
+                    <property name="bottom_attach">11</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_servers_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">11</property>
+                    <property name="bottom_attach">12</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_servers_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max 
servers</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">11</property>
+                    <property name="bottom_attach">12</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_servers_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">11</property>
+                    <property name="bottom_attach">12</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="server_max_log_size_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">12</property>
+                    <property name="bottom_attach">13</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="server_max_log_size_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">max log 
size</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">12</property>
+                    <property name="bottom_attach">13</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="server_max_log_size_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">12</property>
+                    <property name="bottom_attach">13</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="server_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Server</property>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="other_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <widget class="GtkCheckButton" 
id="connection_timeout_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="connection_timeout_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">connection 
timeout</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" 
id="connection_timeout_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="gzip_threshold_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="gzip_threshold_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">gzip 
threshold</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" id="gzip_threshold_spinbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" 
id="symlinks_follow_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="symlinks_follow_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">symlinks 
follow</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="symlinks_follow_combobox">
+                    <property name="visible">True</property>
+                    <property name="active">0</property>
+                    <property name="items" translatable="yes">yes
+no</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="other_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Other</property>
+              </widget>
+              <packing>
+                <property name="position">4</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkStatusbar" id="statusbar">
+            <property name="visible">True</property>
+            <property name="spacing">2</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
diff --git a/misc/PyGTK_Control/ConfigGUI.py 
b/misc/PyGTK_Control/old/ConfigGUI.py
similarity index 100%
rename from misc/PyGTK_Control/ConfigGUI.py
rename to misc/PyGTK_Control/old/ConfigGUI.py
diff --git a/misc/PyGTK_Control/MIMEtypes.py 
b/misc/PyGTK_Control/old/MIMEtypes.py
similarity index 100%
rename from misc/PyGTK_Control/MIMEtypes.py
rename to misc/PyGTK_Control/old/MIMEtypes.py
diff --git a/misc/PyGTK_Control/MIMEtypes.xml 
b/misc/PyGTK_Control/old/MIMEtypes.xml
similarity index 100%
rename from misc/PyGTK_Control/MIMEtypes.xml
rename to misc/PyGTK_Control/old/MIMEtypes.xml
diff --git a/misc/PyGTK_Control/MyServer Configure.ico 
b/misc/PyGTK_Control/old/MyServer Configure.ico
similarity index 100%
rename from misc/PyGTK_Control/MyServer Configure.ico
rename to misc/PyGTK_Control/old/MyServer Configure.ico
diff --git a/misc/PyGTK_Control/XMLGui.glade 
b/misc/PyGTK_Control/old/XMLGui.glade
similarity index 100%
rename from misc/PyGTK_Control/XMLGui.glade
rename to misc/PyGTK_Control/old/XMLGui.glade



commit e89932b722ca91317d7bbb63bae5c2204132b4c8
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 13 13:45:19 2009 +0200

    PyControlLib can now put files to server.

diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index 660589e..847d93d 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -111,9 +111,13 @@ class BasicController():
             self.__check_return_code()
             return self.connection.read()
 
-    def put_file(self, local_path, remote_path):
+    def put_file(self, text, path):
         '''Put file to server.'''
-        pass # not implemented
+        if self.connection:
+            self.connection.send_header('PUTFILE', len(text), path)
+            self.connection.send(text)
+            self.connection.read_header()
+            self.__check_return_code()
 
 class Controller(BasicController):
     def get_MIME_type_configuration(self):



commit 7ed7acae01c06591c9db43849ced487053beca7e
Merge: c93d329 f8ea478
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jul 13 12:38:57 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver
    
    Conflicts:
        misc/py_control_client/MyServer/pycontrol/pycontrol.py

diff --cc misc/py_control_client/MyServer/pycontrol/pycontrol.py
index 76cf76b,4066da5..f5224c3
--- a/misc/py_control_client/MyServer/pycontrol/pycontrol.py
+++ b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
@@@ -43,10 -41,10 +43,10 @@@ class PyMyServerControl(object)
          self.port = port
          self.login = login
          self.password = password
 -        self.sock = socket.ssl(self.s)
 +        self.sock = ssl.wrap_socket(self.s)
  
-     def send_header(self, command, len, args = ""):
 -    #Build the CONTROL header and send it.
+     def send_header(self, command, length, args = ""):
 +        '''Build the CONTROL header and send it.'''
          self.buffer = ""
          self.response_values = {}
          self.response_code = None



commit c93d32970d1d2947f73853ff21cc42e5921dcc96
Merge: 1a50bf0 321a84c
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 21:38:24 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit 1a50bf043a18938786ce237a88284b252092ad4d
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 21:35:07 2009 +0200

    User ssl.wrap_socket instead of depreciated socket.ssl

diff --git a/misc/py_control_client/MyServer/pycontrol/pycontrol.py 
b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
index 686e7e8..76cf76b 100644
--- a/misc/py_control_client/MyServer/pycontrol/pycontrol.py
+++ b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
@@ -15,6 +15,7 @@ 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 socket
+import ssl
 import time
 
 error_codes = {'100': 'CONTROL_OK',
@@ -42,7 +43,7 @@ class PyMyServerControl(object):
         self.port = port
         self.login = login
         self.password = password
-        self.sock = socket.ssl(self.s)
+        self.sock = ssl.wrap_socket(self.s)
 
     def send_header(self, command, len, args = ""):
         '''Build the CONTROL header and send it.'''



commit 420a86f9dd7181b5f8f42a8007cb9ff508721b11
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 21:30:56 2009 +0200

    Fixed method name to be private.

diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index 99cd336..660589e 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -33,7 +33,7 @@ class BasicController():
         self.username = username
         self.password = password
 
-    def __check_return_code__(self):
+    def __check_return_code(self):
         return_code = self.connection.response_code
         if error_codes.get(return_code, '') != 'CONTROL_OK':
             raise ServerError('MyServer returned {0}: {1}'.format(
@@ -56,14 +56,14 @@ class BasicController():
         if self.connection:
             self.connection.send_header('REBOOT', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
 
     def version(self):
         '''Get version string.'''
         if self.connection:
             self.connection.send_header('VERSION', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
             return self.connection.read()
 
     def enable_reboot(self):
@@ -71,21 +71,21 @@ class BasicController():
         if self.connection:
             self.connection.send_header('ENABLEREBOOT', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
 
     def disable_reboot(self):
         '''Disable server auto-reboot.'''
         if self.connection:
             self.connection.send_header('DISABLEREBOOT', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
 
     def show_connections(self):
         '''List active server connections.'''
         if self.connection:
             self.connection.send_header('SHOWCONNECTIONS', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
             return self.connection.read()
 
     def kill_connection(self, num):
@@ -93,14 +93,14 @@ class BasicController():
         if self.connection:
             self.connection.send_header('KILLCONNECTION', 0, str(num))
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
 
     def show_language_files(self):
         '''List available language files.'''
         if self.connection:
             self.connection.send_header('SHOWLANGUAGEFILES', 0)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
             return self.connection.read()
 
     def get_file(self, path):
@@ -108,7 +108,7 @@ class BasicController():
         if self.connection:
             self.connection.send_header('GETFILE', 0, path)
             self.connection.read_header()
-            self.__check_return_code__()
+            self.__check_return_code()
             return self.connection.read()
 
     def put_file(self, local_path, remote_path):



commit e992b4a3e844e463df1189038aa579e337875fe7
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 21:24:03 2009 +0200

    Made docstrings from comments in pycontrol.

diff --git a/misc/py_control_client/MyServer/pycontrol/pycontrol.py 
b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
index f28c636..686e7e8 100644
--- a/misc/py_control_client/MyServer/pycontrol/pycontrol.py
+++ b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
@@ -29,8 +29,9 @@ error_codes = {'100': 'CONTROL_OK',
                '208': 'CONTROL_FILE_NOT_FOUND'}
 
 class PyMyServerControl(object):
-    #Initialize a connection to server:port using login:password as 
credentials.
     def __init__(self, host, port, login, password):
+        '''Initialize a connection to server:port using login:password as
+        credentials.'''
 
         self.connectionType = "Keep-Alive"
 
@@ -43,8 +44,8 @@ class PyMyServerControl(object):
         self.password = password
         self.sock = socket.ssl(self.s)
 
-    #Build the CONTROL header and send it.
     def send_header(self, command, len, args = ""):
+        '''Build the CONTROL header and send it.'''
         self.buffer = ""
         self.response_values = {}
         self.response_code = None
@@ -55,13 +56,13 @@ class PyMyServerControl(object):
 
         self.sock.write(req_header)
 
-    #Send additional data after the header.
     def send(self, data):
+        '''Send additional data after the header.'''
         self.sock.write(data)
 
 
-    #Read a line from the socket.
     def __readline(self):
+        '''Read a line from the socket.'''
         while True:
             self.buffer = self.buffer + self.sock.read(1024)
             ind = self.buffer.find("\r\n")
@@ -71,12 +72,12 @@ class PyMyServerControl(object):
                 self.buffer = self.buffer[ind+2:len(self.buffer)]
                 return ret
 
-    #Returns the number of bytes to be read
     def available_data(self):
+        '''Returns the number of bytes to be read.'''
         return int(self.response_values['LEN']) - self.__bytes_read
 
-    #Read data that follows the the response header.
     def read(self):
+        '''Read data that follows the the response header.'''
         if len(self.buffer) > 0:
             self.__bytes_read = self.__bytes_read + len(self.buffer)
             return self.buffer
@@ -86,8 +87,8 @@ class PyMyServerControl(object):
 
         return data
 
-    #Read the response header.
     def read_header(self):
+        '''Read the response header.'''
         self.response_code = self.__readline()[1:4]
         self.__bytes_read = 0
 
@@ -106,7 +107,7 @@ class PyMyServerControl(object):
             self.response_values[header_name] = value
 
 
-    #Close the socket.
     def close(self):
+        '''Close the socket.'''
         del self.sock
         self.s.close()



commit da3b029e8c7d55ac4c38436303071bb53b04c0f7
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 17:21:34 2009 +0200

    Fixed errors whith getting configuration from server.

diff --git a/misc/py_control_client/MyServer/pycontrollib/config.py 
b/misc/py_control_client/MyServer/pycontrollib/config.py
index deb0ed5..ad4e1fb 100644
--- a/misc/py_control_client/MyServer/pycontrollib/config.py
+++ b/misc/py_control_client/MyServer/pycontrollib/config.py
@@ -47,7 +47,8 @@ class MyServerConfig():
     @staticmethod
     def from_string(text):
         '''Factory to produce MyServerConfig by parsing a string.'''
-        return MyServerConfig.from_lxml_element(etree.XML(text))
+        return MyServerConfig.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
 
     @staticmethod
     def from_lxml_element(root):
diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index 37a0318..99cd336 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -18,7 +18,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 
 from MyServer.pycontrol.pycontrol import PyMyServerControl, error_codes
 from mimetypes import MIMETypes
-from vhosts import VHosts
+from vhost import VHosts
 from config import MyServerConfig
 
 class ServerError(Exception):
@@ -126,4 +126,4 @@ class Controller(BasicController):
 
     def get_server_configuration(self):
         '''Get server settings.'''
-        return MyServerConfig(self.get_file('myserver.xml'))
+        return MyServerConfig.from_string(self.get_file('myserver.xml'))
diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index c2824c9..c9dbaf3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -84,7 +84,8 @@ class Stream():
     @staticmethod
     def from_string(text):
         '''Factory to produce stream by parsing a string.'''
-        return Stream.from_lxml_element(etree.XML(text))
+        return Stream.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
 
     @staticmethod
     def from_lxml_element(root):
@@ -176,7 +177,8 @@ class Log():
     @staticmethod
     def from_string(text):
         '''Factory to produce log by parsing a string.'''
-        return Log.from_lxml_element(etree.XML(text))
+        return Log.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
 
     @staticmethod
     def from_lxml_element(root):
diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 3a62813..1f0bdde 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -200,7 +200,8 @@ class MIMEType():
     @staticmethod
     def from_string(text):
         '''Factory to produce MIMEType by parsing a string.'''
-        return MIMEType.from_lxml_element(etree.XML(text))
+        return MIMEType.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
 
 class MIMETypes():
     def __init__(self, MIME_types = []):
@@ -230,4 +231,5 @@ class MIMETypes():
     @staticmethod
     def from_string(text):
         '''Factory to produce MIMETypes from parsing a string.'''
-        return MIMETypes.from_lxml_element(etree.XML(text))
+        return MIMETypes.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 44181bb..4609774 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -236,7 +236,8 @@ class VHost():
     @staticmethod
     def from_string(text):
         '''Factory to produce VHost by parsing a string.'''
-        return VHost.from_lxml_element(etree.XML(text))
+        return VHost.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))
 
 class VHosts():
     def __init__(self, VHosts):
@@ -267,4 +268,5 @@ class VHosts():
     @staticmethod
     def from_string(text):
         '''Factory to produce VHosts from parsing a string.'''
-        return VHosts.from_lxml_element(etree.XML(text))
+        return VHosts.from_lxml_element(etree.XML(
+                text, parser = etree.XMLParser(remove_comments = True)))



commit c723954a698f9c8384a91453727e32fc6c81009f
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 16:47:52 2009 +0200

    Added definition.search_by_name

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index 949b7a5..ebd62d5 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -118,6 +118,9 @@ class DefinitionElement(Definition):
     def from_string(text):
         '''Factory to produce definition element by parsing a string.'''
         return DefinitionElement.from_lxml_element(etree.XML(text))
+    
+    def search_by_name(self, name):
+        return None if name != self.name else self
 
 class DefinitionTree(Definition):
     '''Definition element containing other definitions.'''
@@ -183,6 +186,13 @@ class DefinitionTree(Definition):
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
+    
+    def search_by_name(self, name):
+        for definition in reversed(self.definitions):
+            ret = definition.search_by_name(name)
+            if ret != None:
+                return ret
+        return None if name != self.name else self
 
 class DefinitionList():
     def __init__(self, definitions = []):
@@ -217,3 +227,10 @@ class DefinitionList():
 
     def __str__(self):
         return '\n'.join(map(str, self.definitions))
+
+    def search_by_name(self, name):
+        for definition in reversed(self.definitions):
+            ret = definition.search_by_name(name)
+            if ret != None:
+                return ret
+        return None
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
index 71e06e8..b7917cf 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
@@ -206,6 +206,11 @@ class DefinitionElementTest(unittest.TestCase):
         self.assertRaises(AttributeError, Definition.from_lxml_element,
                           etree.XML(text))
 
+    def test_search_by_name(self):
+        definition = DefinitionElement('a')
+        self.assertEqual(None, definition.search_by_name('b'))
+        self.assertEqual(definition, definition.search_by_name('a'))
+
 class DefinitionTreeTest(unittest.TestCase):
     def setUp(self):
         self.element_0 = DefinitionElement('test', {'value': 'x'})
@@ -377,6 +382,18 @@ class DefinitionTreeTest(unittest.TestCase):
         self.assertRaises(AttributeError, Definition.from_lxml_element,
                           etree.XML(text))
 
+    def test_search_by_name(self):
+        element_0 = DefinitionElement('a')
+        element_1 = DefinitionElement('b')
+        element_2 = DefinitionElement('a')
+        tree = DefinitionTree('x', definitions = [element_0, element_1, 
element_2])
+        self.assertEqual(element_1, tree.search_by_name('b'))
+        self.assertEqual(element_2, tree.search_by_name('a'))
+        self.assertEqual(None, tree.search_by_name('c'))
+        self.assertEqual(tree, tree.search_by_name('x'))
+        tree = DefinitionTree('b', definitions = [element_0, element_1, 
element_2])
+        self.assertEqual(element_1, tree.search_by_name('b'))
+
 class DefinitionListTest(unittest.TestCase):
     def setUp(self):
         self.definitions = []
@@ -415,6 +432,21 @@ class DefinitionListTest(unittest.TestCase):
         self.assertNotEqual(DefinitionList(), DefinitionList(self.definitions))
         self.assertNotEqual(DefinitionList(), 'other type')
 
+    def test_search_by_name(self):
+        def_list = DefinitionList()
+        self.assertEqual(None, def_list.search_by_name('x'))
+        element_0 = DefinitionElement('x')
+        def_list.add_definition(element_0)
+        self.assertEqual(element_0, def_list.search_by_name('x'))
+        element_1 = DefinitionElement('x')
+        def_list.add_definition(element_1)
+        self.assertEqual(element_1, def_list.search_by_name('x'))
+        element_2 = DefinitionElement('x')
+        tree_1 = DefinitionTree('t', definitions = [element_2])
+        self.assertEqual(element_2, def_list.search_by_name('x'))
+        def_list.add_definition(element_1)
+        self.assertEqual(element_1, def_list.search_by_name('x'))
+
     def test_to_string(self):
         def_list = DefinitionList(self.definitions)
         text = '<wrap>{0}</wrap>'.format(def_list)



commit c592b21b60297ea739ed2a760bb6933f4671aeec
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jul 12 16:25:51 2009 +0200

    Added SSL support in VHost class.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 28b0eee..d5e19ae 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -38,6 +38,14 @@ class VHostTest(unittest.TestCase):
                       [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
                       {'host.domain': None})
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': None}, 'private_key')
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': None}, 'private_key', 'certificate')
 
     def test_name(self):
         vhost = VHost('test vhost')
@@ -141,60 +149,75 @@ class VHostTest(unittest.TestCase):
                       host = {'test.me.org': None})
         self.assertEqual({'test.me.org': None}, vhost.get_host())
 
+    def test_private_key(self):
+        vhost = VHost()
+        self.assertEqual(None, vhost.get_private_key())
+        vhost.set_private_key('path')
+        self.assertEqual('path', vhost.get_private_key())
+        vhost.set_private_key(None)
+        self.assertEqual(None, vhost.get_private_key())
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': None}, 'path')
+        self.assertEqual('path', vhost.get_private_key())
+        vhost = VHost(private_key = 'path')
+        self.assertEqual('path', vhost.get_private_key())
+
+    def test_certificate(self):
+        vhost = VHost()
+        self.assertEqual(None, vhost.get_certificate())
+        vhost.set_certificate('path')
+        self.assertEqual('path', vhost.get_certificate())
+        vhost.set_certificate(None)
+        self.assertEqual(None, vhost.get_certificate())
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': None}, 'key', 'path')
+        self.assertEqual('path', vhost.get_certificate())
+        vhost = VHost(certificate = 'path')
+        self.assertEqual('path', vhost.get_certificate())
+
     def test_equality(self):
         vhost_0 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                         [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+                        {'host.domain': None}, 'private_key', 'certificate')
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                         [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+                        {'host.domain': None}, 'private_key', 'certificate')
         self.assertEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('different', 80, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_name('different')
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 81, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_port(81)
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test_vhost', 80, 'HTTPS', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_protocol('HTTPS')
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/var/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_doc_root('/var/www')
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/var/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_sys_folder('/var/system')
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG', type = 'combined'), 
Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.add_log(Log('NEWLOG'))
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG', type = 
'combined')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.add_ip('10.0.0.0/8')
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/24', '192.168.0.0/16'],
-                        {'host.domain': None})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.add_host('newhost', None)
         self.assertNotEqual(vhost_0, vhost_1)
-        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                        ['127.0.0.0/8', '192.168.0.0/16'],
-                        {'host.domain': True})
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_private_key('new_key')
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost.from_string(str(vhost_0))
+        vhost_1.set_certificate('new certificate')
         self.assertNotEqual(vhost_0, vhost_1)
         self.assertNotEqual(vhost_0, [])
 
@@ -252,6 +275,18 @@ class VHostTest(unittest.TestCase):
         right = VHost(host = {'.*': True, 'a.org': None})
         self.assertEqual(vhost, right)
 
+    def test_from_string_private_key(self):
+        text = '<VHOST><SSL_PRIVATEKEY>private_key</SSL_PRIVATEKEY></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(private_key = 'private_key')
+        self.assertEqual(vhost, right)
+
+    def test_from_string_certificate(self):
+        text = '<VHOST><SSL_CERTIFICATE>certificate</SSL_CERTIFICATE></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(certificate = 'certificate')
+        self.assertEqual(vhost, right)
+
     def test_from_string_full(self):
         text = '''<VHOST>
   <NAME>test vhost</NAME>
@@ -265,12 +300,15 @@ class VHostTest(unittest.TestCase):
   <IP>192.168.0.0/16</IP>
   <HOST useRegex="YES">host.domain</HOST>
   <HOST>test.domain</HOST>
+  <SSL_PRIVATEKEY>private_key</SSL_PRIVATEKEY>
+  <SSL_CERTIFICATE>certificate</SSL_CERTIFICATE>
 </VHOST>'''
         vhost = VHost.from_string(text)
         right = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
-                      {'host.domain': True, 'test.domain': None})
+                      {'host.domain': True, 'test.domain': None},
+                      'private_key', 'certificate')
         self.assertEqual(vhost, right)
 
     def test_from_lxml(self):
@@ -327,6 +365,18 @@ class VHostTest(unittest.TestCase):
         right = VHost(host = {'.*': True, 'a.org': None})
         self.assertEqual(vhost, right)
 
+    def test_from_lxml_private_key(self):
+        text = '<VHOST><SSL_PRIVATEKEY>private_key</SSL_PRIVATEKEY></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(private_key = 'private_key')
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_certificate(self):
+        text = '<VHOST><SSL_CERTIFICATE>certificate</SSL_CERTIFICATE></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(certificate = 'certificate')
+        self.assertEqual(vhost, right)
+
     def test_from_lxml_full(self):
         text = '''<VHOST>
   <NAME>test vhost</NAME>
@@ -340,12 +390,15 @@ class VHostTest(unittest.TestCase):
   <IP>192.168.0.0/16</IP>
   <HOST useRegex="YES">host.domain</HOST>
   <HOST>test.domain</HOST>
+  <SSL_PRIVATEKEY>private_key</SSL_PRIVATEKEY>
+  <SSL_CERTIFICATE>certificate</SSL_CERTIFICATE>
 </VHOST>'''
         vhost = VHost.from_lxml_element(etree.XML(text))
         right = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
-                      {'host.domain': True, 'test.domain': None})
+                      {'host.domain': True, 'test.domain': None},
+                      'private_key', 'certificate')
         self.assertEqual(vhost, right)
 
     def test_bad_root_tag(self):
@@ -399,11 +452,22 @@ class VHostTest(unittest.TestCase):
         copy = VHost.from_string(str(vhost))
         self.assertEqual(vhost, copy)
 
+    def test_to_string_private_key(self):
+        vhost = VHost(private_key = 'private_key')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_certificate(self):
+        vhost = VHost(certificate = 'certificate')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
     def test_to_string_full(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
-                      {'host.domain': True, 'test.domain': None})
+                      {'host.domain': True, 'test.domain': None},
+                      'private_key', 'certificate')
         copy = VHost.from_string(str(vhost))
         self.assertEqual(vhost, copy)
 
@@ -451,12 +515,23 @@ class VHostTest(unittest.TestCase):
         vhost = VHost(host = {'.*': True, 'a.org': None})
         copy = VHost.from_lxml_element(vhost.to_lxml_element())
         self.assertEqual(vhost, copy)
+        
+    def test_to_lxml_private_key(self):
+        vhost = VHost(private_key = 'private_key')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_certificate(self):
+        vhost = VHost(certificate = 'certificate')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
 
     def test_to_lxml_full(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
-                      {'host.domain': True, 'test.domain': None})
+                      {'host.domain': True, 'test.domain': None},
+                      'private_key', 'certificate')
         copy = VHost.from_lxml_element(vhost.to_lxml_element())
         self.assertEqual(vhost, copy)
 
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index cfa1cb0..44181bb 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -22,7 +22,7 @@ from log import Log
 class VHost():
     def __init__(self, name = None, port = None, protocol = None,
                  doc_root = None, sys_folder = None, logs = [],
-                 ip = [], host = {}):
+                 ip = [], host = {}, private_key = None, certificate = None):
         '''Create new instance of VHost. logs and ip are expected to be a
         collection and host is expected to be a dict {name: useRegex} where 
None
         means not set.'''
@@ -40,6 +40,8 @@ class VHost():
         self.host = {}
         for single_host in host.iteritems():
             self.add_host(single_host[0], single_host[1])
+        self.set_private_key(private_key)
+        self.set_certificate(certificate)
 
     def __eq__(self, other):
         return isinstance(other, VHost) and self.name == other.name and \
@@ -47,7 +49,9 @@ class VHost():
             self.doc_root == other.doc_root and \
             self.sys_folder == other.sys_folder and \
             self.logs == other.logs and self.ip == other.ip and \
-            self.host == other.host
+            self.host == other.host and \
+            self.private_key == other.private_key and \
+            self.certificate == other.certificate
 
     def get_name(self):
         '''Get VHost name.'''
@@ -136,6 +140,22 @@ class VHost():
         '''Remove host from VHost host dict.'''
         self.host.pop(host)
 
+    def get_private_key(self):
+        '''Get vhost private ssl key.'''
+        return self.private_key
+    
+    def set_private_key(self, private_key):
+        '''Set vhost private ssl key.'''
+        self.private_key = private_key
+
+    def get_certificate(self):
+        '''Get vhost ssl certificate.'''
+        return self.certificate
+
+    def set_certificate(self, certificate):
+        '''Set vhost ssl certificate.'''
+        self.certificate = certificate
+
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
@@ -156,6 +176,10 @@ class VHost():
             root.append(make_element('DOCROOT', self.doc_root))
         if self.sys_folder is not None:
             root.append(make_element('SYSFOLDER', self.sys_folder))
+        if self.private_key is not None:
+            root.append(make_element('SSL_PRIVATEKEY', self.private_key))
+        if self.certificate is not None:
+            root.append(make_element('SSL_CERTIFICATE', self.certificate))
         for ip_address in self.ip:
             root.append(make_element('IP', ip_address))
         for host, use_regex in self.host.iteritems():
@@ -177,6 +201,8 @@ class VHost():
         protocol = None
         doc_root = None
         sys_folder = None
+        private_key = None
+        certificate = None
         ip = []
         host = {}
         logs = []
@@ -198,9 +224,14 @@ class VHost():
                 if use_regex is not None:
                     use_regex = use_regex == 'YES'
                 host[child.text] = use_regex
+            elif child.tag == 'SSL_PRIVATEKEY':
+                private_key = child.text
+            elif child.tag == 'SSL_CERTIFICATE':
+                certificate = child.text
             else:
                 logs.append(Log.from_lxml_element(child))
-        return VHost(name, port, protocol, doc_root, sys_folder, logs, ip, 
host)
+        return VHost(name, port, protocol, doc_root, sys_folder, logs, ip, 
host,
+                     private_key, certificate)
     
     @staticmethod
     def from_string(text):



commit c3c98305bd84fd381b95f165e9a647542a966ae2
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 10 18:04:19 2009 +0200

    Implemented MyServerConfig.

diff --git a/misc/py_control_client/MyServer/pycontrollib/config.py 
b/misc/py_control_client/MyServer/pycontrollib/config.py
index 2fd7ac9..deb0ed5 100644
--- a/misc/py_control_client/MyServer/pycontrollib/config.py
+++ b/misc/py_control_client/MyServer/pycontrollib/config.py
@@ -17,6 +17,51 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 from lxml import etree
+from definition import Definition, DefinitionList
 
 class MyServerConfig():
-    pass
+    def __init__(self, definitions = []):
+        self.definitions = DefinitionList(definitions)
+
+    def __eq__(self, other):
+        return isinstance(other, MyServerConfig) and \
+            self.definitions == other.definitions
+
+    def get_definitions(self):
+        '''Get current definitions.'''
+        return self.definitions.get_definitions()
+    
+    def add_definition(self, definition, index = None):
+        '''Append definition, if index is not None insert it at index-th
+        position.'''
+        self.definitions.add_definition(definition, index)
+
+    def get_definition(self, index):
+        '''Get index-th definition.'''
+        return self.definitions.get_definition(index)
+
+    def remove_definition(self, index):
+        '''Remove index-th definition.'''
+        self.definitions.remove_definition(index)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce MyServerConfig by parsing a string.'''
+        return MyServerConfig.from_lxml_element(etree.XML(text))
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce MyServerConfig from lxml.etree.Element object.'''
+        if root.tag != 'MYSERVER':
+            raise AttributeError('Expected MYSERVER tag.')
+        return MyServerConfig(map(Definition.from_lxml_element, list(root)))
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    def to_lxml_element(self):
+        '''Convert MyServerConfig to lxml.etree.Element object.'''
+        root = etree.Element('MYSERVER')
+        for definition in self.definitions.get_definitions():
+            root.append(definition.to_lxml_element())
+        return root
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
index d6b833d..6ccf7b7 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/Makefile
+++ b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
@@ -16,9 +16,9 @@
 PYTHON=/usr/bin/python
 PYTHONPATH:=$(shell dirname `pwd`):${PYTHONPATH}
 
-.PHONY: test log_test definition_test mimetypes_test vhost_test
+.PHONY: test log_test definition_test mimetypes_test vhost_test config_test
 
-test: log_test definition_test mimetypes_test vhost_test
+test: log_test definition_test mimetypes_test vhost_test config_test
 
 log_test:
        -${PYTHON} log_test.py
@@ -32,6 +32,9 @@ mimetypes_test:
 vhost_test:
        -${PYTHON} vhost_test.py
 
+config_test:
+       -${PYTHON} config_test.py
+
 .PHONY: clean
 clean:
        rm *.py[co]
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/config_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/config_test.py
new file mode 100644
index 0000000..1f40262
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/test/config_test.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 unittest
+from lxml import etree
+from config import MyServerConfig
+from definition import DefinitionElement, DefinitionTree
+
+class MyServerConfigTest(unittest.TestCase):
+    def setUp(self):
+        self.definitions = []
+        self.definitions.append(DefinitionElement('a'))
+        self.definitions.append(DefinitionElement('b'))
+        self.definitions.append(DefinitionElement('c'))
+
+    def test_creation(self):
+        config = MyServerConfig()
+        config = MyServerConfig(self.definitions)
+
+    def test_definitions(self):
+        config = MyServerConfig()
+        self.assertEqual([], config.get_definitions())
+        counter = 0
+        for definition in self.definitions:
+            config.add_definition(definition)
+            counter += 1
+            self.assertEqual(self.definitions[:counter],
+                             config.get_definitions())
+        for counter in xrange(len(self.definitions)):
+            self.assertEqual(self.definitions[counter],
+                             config.get_definition(counter))
+        config.add_definition(DefinitionElement('test'), 1)
+        self.assertEqual(DefinitionElement('test'), config.get_definition(1))
+        config.remove_definition(1)
+        self.assertEqual(self.definitions, config.get_definitions())
+        for counter in xrange(len(self.definitions)):
+            config.remove_definition(0)
+            self.assertEqual(self.definitions[counter + 1:],
+                             config.get_definitions())
+
+    def test_equality(self):
+        self.assertEqual(MyServerConfig(self.definitions),
+                         MyServerConfig(self.definitions))
+        self.assertNotEqual(MyServerConfig(), MyServerConfig(self.definitions))
+        self.assertNotEqual(MyServerConfig(), 'other type')
+
+    def test_from_string(self):
+        text = '<MYSERVER />'
+        config = MyServerConfig.from_string(text)
+        right = MyServerConfig()
+        self.assertEqual(config, right)
+
+    def test_from_string_definitions(self):
+        text = '''<MYSERVER>
+  <DEFINE name="a" />
+  <DEFINE>
+    <DEFINE name="b" />
+    <DEFINE name="c" />
+  </DEFINE>
+</MYSERVER>'''
+        config = MyServerConfig.from_string(text)
+        right = MyServerConfig([DefinitionElement('a'), DefinitionTree(
+                    definitions = [DefinitionElement('b'),
+                                   DefinitionElement('c')])])
+        self.assertEqual(config, right)
+
+    def test_from_lxml(self):
+        text = '<MYSERVER />'
+        config = MyServerConfig.from_lxml_element(etree.XML(text))
+        right = MyServerConfig()
+        self.assertEqual(config, right)
+
+    def test_from_lxml_definitions(self):
+        text = '''<MYSERVER>
+  <DEFINE name="a" />
+  <DEFINE>
+    <DEFINE name="b" />
+    <DEFINE name="c" />
+  </DEFINE>
+</MYSERVER>'''
+        config = MyServerConfig.from_lxml_element(etree.XML(text))
+        right = MyServerConfig([DefinitionElement('a'), DefinitionTree(
+                    definitions = [DefinitionElement('b'),
+                                   DefinitionElement('c')])])
+        self.assertEqual(config, right)
+
+    def test_to_string(self):
+        config = MyServerConfig()
+        copy = MyServerConfig.from_string(str(config))
+        self.assertEqual(config, copy)
+
+    def test_to_string_definitions(self):
+        config = MyServerConfig([DefinitionElement('a'), DefinitionTree(
+                    definitions = [DefinitionElement('b'),
+                                   DefinitionElement('c')])])
+        copy = MyServerConfig.from_string(str(config))
+        self.assertEqual(config, copy)
+
+    def test_to_string(self):
+        config = MyServerConfig()
+        copy = MyServerConfig.from_lxml_element(config.to_lxml_element())
+        self.assertEqual(config, copy)
+
+    def test_to_string_definitions(self):
+        config = MyServerConfig([DefinitionElement('a'), DefinitionTree(
+                    definitions = [DefinitionElement('b'),
+                                   DefinitionElement('c')])])
+        copy = MyServerConfig.from_lxml_element(config.to_lxml_element())
+        self.assertEqual(config, copy)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR />'
+        self.assertRaises(AttributeError, MyServerConfig.from_string, text)
+        self.assertRaises(AttributeError, MyServerConfig.from_lxml_element,
+                          etree.XML(text))
+
+
+if __name__ == '__main__':
+    unittest.main()



commit 7794e0a8c9ea18c4792754f8890c8f8e4654c341
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 10 16:34:00 2009 +0200

    Mimetypes use DefinitionList.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 8b6fe05..3a62813 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public 
License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
-from definition import Definition
+from definition import Definition, DefinitionList
 from lxml import etree
 
 class MIMEType():
@@ -35,9 +35,7 @@ class MIMEType():
         for filter in filters:
             self.add_filter(filter)
         self.set_self_executed(self_executed)
-        self.definitions = []
-        for definition in definitions:
-            self.add_definition(definition)
+        self.definitions = DefinitionList(definitions)
 
     def get_mime(self):
         '''Get associated mime type.'''
@@ -116,23 +114,20 @@ class MIMEType():
 
     def get_definitions(self):
         '''Get all definitions.'''
-        return self.definitions
+        return self.definitions.get_definitions()
     
     def get_definition(self, index):
         '''Get definition with given index.'''
-        return self.definitions[index]
+        return self.definitions.get_definition(index)
 
     def add_definition(self, definition, index = None):
         '''Append definition after all other definitions, or insert it at
         index.'''
-        if index is None:
-            self.definitions.append(definition)
-        else:
-            self.definitions.insert(index, definition)
+        self.definitions.add_definition(definition, index)
 
     def remove_definition(self, index):
         '''Remove definition with given index.'''
-        self.definitions.pop(index)
+        self.definitions.remove_definition(index)
 
     def __eq__(self, other):
         return isinstance(other, MIMEType) and \
@@ -168,7 +163,7 @@ class MIMEType():
             root.append(element)
         for element in map(make_filter_element, self.filters):
             root.append(element)
-        for definition in self.definitions:
+        for definition in self.definitions.get_definitions():
             root.append(definition.to_lxml_element())
         return root
 



commit e2f770d7d8fde08d627b40151656928fecdc2c39
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 10 16:03:06 2009 +0200

    Added DefinitionList class.

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index fd4c353..949b7a5 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -184,3 +184,36 @@ class DefinitionTree(Definition):
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
+class DefinitionList():
+    def __init__(self, definitions = []):
+        '''Construct new DefinitionList object with given definitions.'''
+        self.definitions = []
+        for definition in definitions:
+            self.add_definition(definition)
+
+    def add_definition(self, definition, index = None):
+        '''Append definition to current list of definitions, if index is not
+        None insert at index-th position.'''
+        if index is None:
+            self.definitions.append(definition)
+        else:
+            self.definitions.insert(index, definition)
+
+    def get_definitions(self):
+        '''Get current list of definitions.'''
+        return self.definitions
+
+    def get_definition(self, index):
+        '''Get index-th definition.'''
+        return self.definitions[index]
+
+    def remove_definition(self, index):
+        '''Remove index-th definition.'''
+        self.definitions.pop(index)
+
+    def __eq__(self, other):
+        return isinstance(other, DefinitionList) and \
+            self.definitions == other.definitions
+
+    def __str__(self):
+        return '\n'.join(map(str, self.definitions))
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
index 6177b56..71e06e8 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public 
License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
-from definition import Definition, DefinitionElement, DefinitionTree
+from definition import Definition, DefinitionElement, DefinitionTree, 
DefinitionList
 from lxml import etree
 import unittest
 
@@ -81,7 +81,7 @@ class DefinitionTest(unittest.TestCase):
 
     def test_equality(self):
         self.assertNotEqual(Definition(), 'different type')
-    
+
 class DefinitionElementTest(unittest.TestCase):
     def test_creation(self):
         definition = DefinitionElement()
@@ -126,7 +126,7 @@ class DefinitionElementTest(unittest.TestCase):
         definition = DefinitionElement.from_string(text)
         right = DefinitionElement('test', {'value': 'x', 'a': 'b'})
         self.assertEqual(definition, right)
-        
+
     def test_from_lxml(self):
         text = '<DEFINE />'
         definition = DefinitionElement.from_lxml_element(etree.XML(text))
@@ -164,7 +164,7 @@ class DefinitionElementTest(unittest.TestCase):
         definition = DefinitionElement()
         copy = DefinitionElement.from_string(str(definition))
         self.assertEqual(definition, copy)
-    
+
     def test_to_string_name(self):
         definition = DefinitionElement(name = 'test')
         copy = DefinitionElement.from_string(str(definition))
@@ -184,7 +184,7 @@ class DefinitionElementTest(unittest.TestCase):
         definition = DefinitionElement()
         copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
-    
+
     def test_to_lxml_name(self):
         definition = DefinitionElement(name = 'test')
         copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
@@ -199,7 +199,7 @@ class DefinitionElementTest(unittest.TestCase):
         definition = DefinitionElement('test', {'value': 'x', 'a': 'b'})
         copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
-    
+
     def test_bad_root_tag(self):
         text = '<ERROR name="http.error.file.404" value="404.html" />'
         self.assertRaises(AttributeError, Definition.from_string, text)
@@ -344,7 +344,7 @@ class DefinitionTreeTest(unittest.TestCase):
         definition = DefinitionTree('test', [self.element_0, self.element_1], 
{'a': 'b'})
         copy = DefinitionTree.from_string(str(definition))
         self.assertEqual(definition, copy)
-        
+
     def test_to_lxml(self):
         definition = DefinitionTree()
         copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
@@ -377,5 +377,51 @@ class DefinitionTreeTest(unittest.TestCase):
         self.assertRaises(AttributeError, Definition.from_lxml_element,
                           etree.XML(text))
 
+class DefinitionListTest(unittest.TestCase):
+    def setUp(self):
+        self.definitions = []
+        self.definitions.append(DefinitionElement('a'))
+        self.definitions.append(DefinitionElement('b'))
+        self.definitions.append(DefinitionElement('c'))
+
+    def test_creation(self):
+        def_list = DefinitionList()
+        def_list = DefinitionList(self.definitions)
+
+    def test_definitions(self):
+        def_list = DefinitionList()
+        self.assertEqual([], def_list.get_definitions())
+        counter = 0
+        for definition in self.definitions:
+            def_list.add_definition(definition)
+            counter += 1
+            self.assertEqual(self.definitions[:counter],
+                             def_list.get_definitions())
+        for counter in xrange(len(self.definitions)):
+            self.assertEqual(self.definitions[counter],
+                             def_list.get_definition(counter))
+        def_list.add_definition(DefinitionElement('test'), 1)
+        self.assertEqual(DefinitionElement('test'), def_list.get_definition(1))
+        def_list.remove_definition(1)
+        self.assertEqual(self.definitions, def_list.get_definitions())
+        for counter in xrange(len(self.definitions)):
+            def_list.remove_definition(0)
+            self.assertEqual(self.definitions[counter + 1:],
+                             def_list.get_definitions())
+
+    def test_equality(self):
+        self.assertEqual(DefinitionList(self.definitions),
+                         DefinitionList(self.definitions))
+        self.assertNotEqual(DefinitionList(), DefinitionList(self.definitions))
+        self.assertNotEqual(DefinitionList(), 'other type')
+
+    def test_to_string(self):
+        def_list = DefinitionList(self.definitions)
+        text = '<wrap>{0}</wrap>'.format(def_list)
+        copy = DefinitionList()
+        for lxml_definition in list(etree.XML(text)):
+            copy.add_definition(Definition.from_lxml_element(lxml_definition))
+        self.assertEqual(def_list, copy)
+
 if __name__ == '__main__':
     unittest.main()



commit 0010615eede49df77e800edc331c35603434bf26
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 9 21:51:14 2009 +0200

    Implemented get methods of Controller.

diff --git a/misc/py_control_client/MyServer/pycontrollib/config.py 
b/misc/py_control_client/MyServer/pycontrollib/config.py
new file mode 100644
index 0000000..2fd7ac9
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/config.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+class MyServerConfig():
+    pass
diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
index e9cda37..37a0318 100644
--- a/misc/py_control_client/MyServer/pycontrollib/controller.py
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -17,6 +17,9 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 from MyServer.pycontrol.pycontrol import PyMyServerControl, error_codes
+from mimetypes import MIMETypes
+from vhosts import VHosts
+from config import MyServerConfig
 
 class ServerError(Exception):
     '''Raised when MyServer return code doesn't mean success.'''
@@ -112,3 +115,15 @@ class BasicController():
         '''Put file to server.'''
         pass # not implemented
 
+class Controller(BasicController):
+    def get_MIME_type_configuration(self):
+        '''Get MIME types settings.'''
+        return MIMETypes.from_string(self.get_file('mimetypes.xml'))
+
+    def get_vhost_configuration(self):
+        '''Get VHosts settings.'''
+        return VHosts.from_string(self.get_file('virtualhosts.xml'))
+
+    def get_server_configuration(self):
+        '''Get server settings.'''
+        return MyServerConfig(self.get_file('myserver.xml'))



commit 12e27e127621f4b010d27bfdcc15f5bbb307f125
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 9 21:38:41 2009 +0200

    Implemented BasicController.
    
    Not implemented put_file method.

diff --git a/misc/py_control_client/MyServer/pycontrollib/controller.py 
b/misc/py_control_client/MyServer/pycontrollib/controller.py
new file mode 100644
index 0000000..e9cda37
--- /dev/null
+++ b/misc/py_control_client/MyServer/pycontrollib/controller.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from MyServer.pycontrol.pycontrol import PyMyServerControl, error_codes
+
+class ServerError(Exception):
+    '''Raised when MyServer return code doesn't mean success.'''
+    pass
+
+class BasicController():
+    def __init__(self, host, port, username, password):
+        self.connection = None
+        self.host = host
+        self.port = port
+        self.username = username
+        self.password = password
+
+    def __check_return_code__(self):
+        return_code = self.connection.response_code
+        if error_codes.get(return_code, '') != 'CONTROL_OK':
+            raise ServerError('MyServer returned {0}: {1}'.format(
+                    return_code, error_codes.get(return_code, '')))
+
+    def connect(self):
+        '''Connect to server.'''
+        self.disconnect()
+        self.connection = PyMyServerControl(self.host, self.port,
+                                            self.username, self.password)
+
+    def disconnect(self):
+        '''Disconnect from server.'''
+        if self.connection is not None:
+            self.connection.close()
+            self.connection = None
+
+    def reboot(self):
+        '''Reboot server.'''
+        if self.connection:
+            self.connection.send_header('REBOOT', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+
+    def version(self):
+        '''Get version string.'''
+        if self.connection:
+            self.connection.send_header('VERSION', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+            return self.connection.read()
+
+    def enable_reboot(self):
+        '''Enable server auto-reboot.'''
+        if self.connection:
+            self.connection.send_header('ENABLEREBOOT', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+
+    def disable_reboot(self):
+        '''Disable server auto-reboot.'''
+        if self.connection:
+            self.connection.send_header('DISABLEREBOOT', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+
+    def show_connections(self):
+        '''List active server connections.'''
+        if self.connection:
+            self.connection.send_header('SHOWCONNECTIONS', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+            return self.connection.read()
+
+    def kill_connection(self, num):
+        '''Kill connection to server.'''
+        if self.connection:
+            self.connection.send_header('KILLCONNECTION', 0, str(num))
+            self.connection.read_header()
+            self.__check_return_code__()
+
+    def show_language_files(self):
+        '''List available language files.'''
+        if self.connection:
+            self.connection.send_header('SHOWLANGUAGEFILES', 0)
+            self.connection.read_header()
+            self.__check_return_code__()
+            return self.connection.read()
+
+    def get_file(self, path):
+        '''Get file from server.'''
+        if self.connection:
+            self.connection.send_header('GETFILE', 0, path)
+            self.connection.read_header()
+            self.__check_return_code__()
+            return self.connection.read()
+
+    def put_file(self, local_path, remote_path):
+        '''Put file to server.'''
+        pass # not implemented
+
diff --git a/misc/py_control_client/MyServer/pycontrollib/pycontrollib.py 
b/misc/py_control_client/MyServer/pycontrollib/pycontrollib.py
deleted file mode 100644
index a32133c..0000000
--- a/misc/py_control_client/MyServer/pycontrollib/pycontrollib.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# -*- coding: utf-8 -*-
-'''
-MyServer
-Copyright (C) 2009 Free Software Foundation, Inc.
-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 socket
-from myserver.pycontrol import PyMyServerControl
-from configWrapper import VHostWrapper, make_vhost_list
-
-import sys
-
-class Controller():
-    '''High level library to control GNU MyServer. Every method returns True
-    or requested data on success and False on error.'''
-    class Parser():
-        '''Parse command output.'''
-        def connections(self, lines): # TODO: split lines to dicts
-            '''Parse data returned by SHOWCONNECTIONS command.'''
-            l = []
-            for line in lines.splitlines():
-                parts = line.split(' - ')
-                l.append(dict(zip(['id', 'client_ip', 'client_port',
-                    'server_ip', 'server_port', '?a', '?b'], parts)))
-            return l
-
-        def language_files(self, lines):
-            '''Parse data returned by SHOWLANGUAGEFILES command.'''
-            return lines.splitlines()
-
-    def __init__(self, host, port, username, password):
-        self.connection = None
-        self.parser = self.Parser()
-        self.host = host
-        self.port = port
-        self.username = username
-        self.password = password
-        self.response_code = 100
-
-    def get_response_code(self):
-        '''Get response code of last issued command.'''
-        return self.response_code
-
-    def connect(self):
-        '''Connect to server.'''
-        self.disconnect()
-        try:
-            self.connection = PyMyServerControl(self.host, self.port,
-                self.username, self.password)
-        except socket.error:
-            return False
-        return True
-
-    def disconnect(self):
-        '''Disconnect from server.'''
-        if self.connection:
-            self.connection.close()
-            self.connection = None
-        return True
-
-    def reboot(self):
-        '''Reboot server.'''
-        if self.connection:
-            self.connection.send_header('REBOOT', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return True
-        else:
-            return False
-
-    def version(self):
-        '''Get version string.'''
-        if self.connection:
-            self.connection.send_header('VERSION', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return self.connection.read()
-        else:
-            return False
-
-    def enable_reboot(self):
-        '''Enable server auto-reboot.'''
-        if self.connection:
-            self.connection.send_header('ENABLEREBOOT', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return True
-        else:
-            return False
-
-    def disable_reboot(self):
-        '''Disable server auto-reboot.'''
-        if self.connection:
-            self.connection.send_header('DISABLEREBOOT', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return True
-        else:
-            return False
-
-    def show_connections(self):
-        '''List active server connections.'''
-        if self.connection:
-            self.connection.send_header('SHOWCONNECTIONS', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return self.parser.connections(self.connection.read())
-        else:
-            return False
-
-    def kill_connection(self, num):
-        '''Kill connection to server.'''
-        if self.connection:
-            self.connection.send_header('KILLCONNECTION', 0, str(num))
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return True
-        else:
-            return False
-
-    def show_language_files(self):
-        '''List available language files.'''
-        if self.connection:
-            self.connection.send_header('SHOWLANGUAGEFILES', 0)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return self.parser.language_files(self.connection.read())
-        else:
-            return False
-
-    def get_file(self, file_name):
-        '''Get file from server.'''
-        if self.connection:
-            self.connection.send_header('GETFILE', 0, file_name)
-            self.connection.read_header()
-            self.response_code = self.connection.response_code
-            if self.response_code != '100':
-                return False
-            return self.connection.read()
-        else:
-            return False
-
-    def get_vhost_configuration(self):
-        '''Get virtual hosts configuration wrapped in list of VHostWrapper
-        objects.'''
-        text = self.get_file('virtualhosts.xml')
-        if text is not False:
-            return make_vhost_list(text)
-        return False
-



commit a4b940249c6c161fc18274ddef85ef34785f0a6e
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 9 20:18:56 2009 +0200

    Split vhost  to/from string/lxml tests.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 8a7989e..28b0eee 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -199,95 +199,267 @@ class VHostTest(unittest.TestCase):
         self.assertNotEqual(vhost_0, [])
 
     def test_from_string(self):
+        text = '<VHOST />'
+        vhost = VHost.from_string(text)
+        right = VHost()
+        self.assertEqual(vhost, right)
+
+    def test_from_string_name(self):
+        text = '<VHOST><NAME>test vhost</NAME></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost('test vhost')
+        self.assertEqual(vhost, right)
+
+    def test_from_string_port(self):
+        text = '<VHOST><PORT>80</PORT></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(port = 80)
+        self.assertEqual(vhost, right)
+
+    def test_from_string_protocol(self):
+        text = '<VHOST><PROTOCOL>HTTP</PROTOCOL></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(protocol = 'HTTP')
+        self.assertEqual(vhost, right)
+
+    def test_from_string_doc_root(self):
+        text = '<VHOST><DOCROOT>/www</DOCROOT></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(doc_root = '/www')
+        self.assertEqual(vhost, right)
+
+    def test_from_string_sys_folder(self):
+        text = '<VHOST><SYSFOLDER>/system</SYSFOLDER></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(sys_folder = '/system')
+        self.assertEqual(vhost, right)
+
+    def test_from_string_logs(self):
+        text = '<VHOST><ACCESSLOG /><SOMELOG /></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(logs = [Log('ACCESSLOG'), Log('SOMELOG')])
+        self.assertEqual(vhost, right)
+
+    def test_from_string_ip(self):
+        text = '<VHOST><IP>127.0.0.0/8</IP></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(ip = ['127.0.0.0/8'])
+        self.assertEqual(vhost, right)
+
+    def test_from_string_host(self):
+        text = '<VHOST><HOST 
useRegex="YES">.*</HOST><HOST>a.org</HOST></VHOST>'
+        vhost = VHost.from_string(text)
+        right = VHost(host = {'.*': True, 'a.org': None})
+        self.assertEqual(vhost, right)
+
+    def test_from_string_full(self):
         text = '''<VHOST>
   <NAME>test vhost</NAME>
   <PORT>80</PORT>
-  <IP>127.0.0.0/8</IP>
-  <IP>192.168.0.0/16</IP>
   <PROTOCOL>HTTP</PROTOCOL>
   <DOCROOT>/www</DOCROOT>
   <SYSFOLDER>/system</SYSFOLDER>
-  <HOST useRegex="YES">host.domain</HOST>
-  <HOST>test.domain</HOST>
   <ACCESSLOG />
   <WARNINGLOG />
-</VHOST>'''
-        vhost = VHost.from_string(text)
-        self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
-                                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                                      ['127.0.0.0/8', '192.168.0.0/16'],
-                                      {'host.domain': True, 'test.domain': 
None}))
-
-    def test_from_lxml(self):
-        text = '''<VHOST>
-  <NAME>test vhost</NAME>
-  <PORT>80</PORT>
   <IP>127.0.0.0/8</IP>
   <IP>192.168.0.0/16</IP>
-  <PROTOCOL>HTTP</PROTOCOL>
-  <DOCROOT>/www</DOCROOT>
-  <SYSFOLDER>/system</SYSFOLDER>
   <HOST useRegex="YES">host.domain</HOST>
   <HOST>test.domain</HOST>
-  <ACCESSLOG />
-  <WARNINGLOG />
 </VHOST>'''
+        vhost = VHost.from_string(text)
+        right = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': True, 'test.domain': None})
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml(self):
+        text = '<VHOST />'
         vhost = VHost.from_lxml_element(etree.XML(text))
-        self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
-                                 [Log('ACCESSLOG'), Log('WARNINGLOG')],
-                                 ['127.0.0.0/8', '192.168.0.0/16'],
-                                 {'host.domain': True, 'test.domain': None}))
+        right = VHost()
+        self.assertEqual(vhost, right)
 
-    def test_to_string(self):
+    def test_from_lxml_name(self):
+        text = '<VHOST><NAME>test vhost</NAME></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost('test vhost')
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_port(self):
+        text = '<VHOST><PORT>80</PORT></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(port = 80)
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_protocol(self):
+        text = '<VHOST><PROTOCOL>HTTP</PROTOCOL></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(protocol = 'HTTP')
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_doc_root(self):
+        text = '<VHOST><DOCROOT>/www</DOCROOT></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(doc_root = '/www')
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_sys_folder(self):
+        text = '<VHOST><SYSFOLDER>/system</SYSFOLDER></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(sys_folder = '/system')
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_logs(self):
+        text = '<VHOST><ACCESSLOG /><SOMELOG /></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(logs = [Log('ACCESSLOG'), Log('SOMELOG')])
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_ip(self):
+        text = '<VHOST><IP>127.0.0.0/8</IP></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(ip = ['127.0.0.0/8'])
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_host(self):
+        text = '<VHOST><HOST 
useRegex="YES">.*</HOST><HOST>a.org</HOST></VHOST>'
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost(host = {'.*': True, 'a.org': None})
+        self.assertEqual(vhost, right)
+
+    def test_from_lxml_full(self):
         text = '''<VHOST>
   <NAME>test vhost</NAME>
   <PORT>80</PORT>
-  <IP>127.0.0.0/8</IP>
-  <IP>192.168.0.0/16</IP>
   <PROTOCOL>HTTP</PROTOCOL>
   <DOCROOT>/www</DOCROOT>
   <SYSFOLDER>/system</SYSFOLDER>
-  <HOST useRegex="YES">host.domain</HOST>
-  <HOST>test.domain</HOST>
   <ACCESSLOG />
   <WARNINGLOG />
-</VHOST>'''
-        vhost = VHost.from_string(text)
-        copy = VHost.from_string(str(vhost))
-        self.assertEqual(vhost, copy)
-
-    def test_to_lxml(self):
-        text = '''<VHOST>
-  <NAME>test vhost</NAME>
-  <PORT>80</PORT>
   <IP>127.0.0.0/8</IP>
   <IP>192.168.0.0/16</IP>
-  <PROTOCOL>HTTP</PROTOCOL>
-  <DOCROOT>/www</DOCROOT>
-  <SYSFOLDER>/system</SYSFOLDER>
   <HOST useRegex="YES">host.domain</HOST>
   <HOST>test.domain</HOST>
-  <ACCESSLOG />
-  <WARNINGLOG />
 </VHOST>'''
-        vhost = VHost.from_string(text)
-        copy = VHost.from_lxml_element(vhost.to_lxml_element())
-        self.assertEqual(vhost, copy)
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        right = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': True, 'test.domain': None})
+        self.assertEqual(vhost, right)
 
     def test_bad_root_tag(self):
-        text = '''<ERROR>
-  <NAME>test vhost</NAME>
-  <PORT>80</PORT>
-  <PROTOCOL>HTTP</PROTOCOL>
-  <DOCROOT>/www</DOCROOT>
-  <SYSFOLDER>/system</SYSFOLDER>
-  <ACCESSLOG />
-  <WARNINGLOG />
-</ERROR>'''
+        text = '<ERROR />'
         self.assertRaises(AttributeError, VHost.from_string, text)
         self.assertRaises(AttributeError, VHost.from_lxml_element,
                           etree.XML(text))
 
+    def test_to_string(self):
+        vhost = VHost()
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_name(self):
+        vhost = VHost('test vhost')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_port(self):
+        vhost = VHost(port = 80)
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_protocol(self):
+        vhost = VHost(protocol = 'HTTP')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_doc_root(self):
+        vhost = VHost(doc_root = '/www')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_sys_folder(self):
+        vhost = VHost(doc_root = '/system')
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_logs(self):
+        vhost = VHost(logs = [Log('ACCESSLOG'), Log('SOMELOG')])
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_ip(self):
+        vhost = VHost(ip = ['127.0.0.0/8'])
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_host(self):
+        vhost = VHost(host = {'.*': True, 'a.org': None})
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_string_full(self):
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': True, 'test.domain': None})
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml(self):
+        vhost = VHost()
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_name(self):
+        vhost = VHost('test vhost')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_port(self):
+        vhost = VHost(port = 80)
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_protocol(self):
+        vhost = VHost(protocol = 'HTTP')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_doc_root(self):
+        vhost = VHost(doc_root = '/www')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_sys_folder(self):
+        vhost = VHost(doc_root = '/system')
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_logs(self):
+        vhost = VHost(logs = [Log('ACCESSLOG'), Log('SOMELOG')])
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_ip(self):
+        vhost = VHost(ip = ['127.0.0.0/8'])
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_host(self):
+        vhost = VHost(host = {'.*': True, 'a.org': None})
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml_full(self):
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': True, 'test.domain': None})
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
 class VHostsTest(unittest.TestCase):
     def setUp(self):
         self.vhost_0 = VHost('vhost 0', 80, 'HTTP', '/www', '/system',



commit 212e40c400279374b006dc3f321e71ab4e41cc13
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 9 19:42:50 2009 +0200

    Made VHost class more flexible.
    
    All attributes are optional, logs are not restricted to access log and
    warning log.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index b8979d3..8a7989e 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -23,95 +23,89 @@ from lxml import etree
 
 class VHostTest(unittest.TestCase):
     def test_creation(self):
+        vhost = VHost()
+        vhost = VHost('test vhost')
+        vhost = VHost('test vhost', 80)
+        vhost = VHost('test vhost', 80, 'HTTP')
+        vhost = VHost('test vhost', 80, 'HTTP', '/www')
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system')
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')])
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'])
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
                       {'host.domain': None})
 
     def test_name(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = VHost('test vhost')
         self.assertEqual('test vhost', vhost.get_name())
         vhost.set_name('vhost')
         self.assertEqual('vhost', vhost.get_name())
-        self.assertRaises(AttributeError, vhost.set_name, None)
-        self.assertRaises(AttributeError, VHost, None, 80, 'HTTP',
-                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
+        vhost.set_name(None)
+        self.assertEqual(None, vhost.get_name())
+        vhost = VHost(name = 'test vhost')
+        self.assertEqual('test vhost', vhost.get_name())
 
     def test_port(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = VHost('test vhost', 80)
         self.assertEqual(80, vhost.get_port())
         vhost.set_port(8080)
         self.assertEqual(8080, vhost.get_port())
-        self.assertRaises(AttributeError, vhost.set_port, None)
-        self.assertRaises(AttributeError, VHost, 'test vhost', None, 'HTTP',
-                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
+        vhost.set_port(None)
+        self.assertEqual(None, vhost.get_port())
+        vhost = VHost(port = 80)
+        self.assertEqual(80, vhost.get_port())
 
     def test_protocol(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = VHost('test vhost', 80, 'HTTP')
         self.assertEqual('HTTP', vhost.get_protocol())
-        vhost.set_protocol('HTTPS')
-        self.assertEqual('HTTPS', vhost.get_protocol())
         vhost.set_protocol('NEW PROTOCOL')
         self.assertEqual('NEW PROTOCOL', vhost.get_protocol())
-        self.assertRaises(AttributeError, vhost.set_protocol, None)
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, None, 
'/www',
-                          '/system', Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost.set_protocol(None)
+        self.assertEqual(None, vhost.get_protocol())
+        vhost = VHost(protocol = 'HTTP')
+        self.assertEqual('HTTP', vhost.get_protocol())
 
     def test_doc_root(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = VHost('test vhost', 80, 'HTTP', '/www')
         self.assertEqual('/www', vhost.get_doc_root())
         vhost.set_doc_root('/var/www')
         self.assertEqual('/var/www', vhost.get_doc_root())
-        self.assertRaises(AttributeError, vhost.set_doc_root, None)
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          None, '/system', Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost.set_doc_root(None)
+        self.assertEqual(None, vhost.get_doc_root())
+        vhost = VHost(doc_root = '/www')
+        self.assertEqual('/www', vhost.get_doc_root())
 
     def test_sys_folder(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system')
         self.assertEqual('/system', vhost.get_sys_folder())
         vhost.set_sys_folder('/var/system')
         self.assertEqual('/var/system', vhost.get_sys_folder())
-        self.assertRaises(AttributeError, vhost.set_sys_folder, None)
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          '/www', None, Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost.set_sys_folder(None)
+        self.assertEqual(None, vhost.get_sys_folder())
+        vhost = VHost(sys_folder = '/system')
+        self.assertEqual('/system', vhost.get_sys_folder())
 
-    def test_log(self):
-        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertEqual(Log('ACCESSLOG'), vhost.get_access_log())
-        self.assertEqual(Log('WARNINGLOG'), vhost.get_warning_log())
-        vhost.set_access_log(Log('ACCESSLOG', type = 'combined'))
-        vhost.set_warning_log(Log('WARNINGLOG', type = 'combined'))
-        self.assertEqual(Log('ACCESSLOG', type = 'combined'),
-                         vhost.get_access_log())
-        self.assertEqual(Log('WARNINGLOG', type = 'combined'),
-                         vhost.get_warning_log())
-        self.assertRaises(AttributeError, vhost.set_access_log, None)
-        self.assertRaises(AttributeError, vhost.set_warning_log, None)
-        self.assertRaises(AttributeError, vhost.set_access_log, Log('ERROR'))
-        self.assertRaises(AttributeError, vhost.set_warning_log, Log('ERROR'))
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          '/www', '/system', None, Log('WARNINGLOG'))
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          '/www', '/system', Log('ACCESSLOG'), None)
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          '/www', '/system', Log('ERROR'), Log('WARNINGLOG'))
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
-                          '/www', '/system', Log('ACCESSLOG'), Log('ERROR'))
+    def test_logs(self):
+        a_log = Log('ACCESSLOG')
+        w_log = Log('WARNINGLOG')
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system', [a_log, 
w_log])
+        self.assertEqual([a_log, w_log], vhost.get_logs())
+        vhost.add_log(a_log)
+        self.assertEqual([a_log, w_log, a_log], vhost.get_logs())
+        vhost.remove_log(0)
+        self.assertEqual([w_log, a_log], vhost.get_logs())
+        vhost.add_log(w_log, 0)
+        self.assertEqual([w_log, w_log, a_log], vhost.get_logs())
+        vhost = VHost(logs = [a_log, w_log])
+        self.assertEqual([a_log, w_log], vhost.get_logs())
 
     def test_ip(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+                      [Log('ACCESSLOG')])
         self.assertEqual(set(), vhost.get_ip())
         vhost.add_ip('10.0.0.0/8')
         vhost.add_ip('192.168.0.0/16')
@@ -119,17 +113,17 @@ class VHostTest(unittest.TestCase):
         vhost.remove_ip('10.0.0.0/8')
         self.assertEqual(set(['192.168.0.0/16']), vhost.get_ip())
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'])
         self.assertEqual(set(['127.0.0.0/8', '192.168.0.0/16']), 
vhost.get_ip())
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ip = ['127.0.0.0/8', '192.168.0.0/16'])
         self.assertEqual(set(['127.0.0.0/8', '192.168.0.0/16']), 
vhost.get_ip())
 
     def test_host(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+                      [Log('ACCESSLOG')])
         self.assertEqual({}, vhost.get_host())
         vhost.add_host('foo.bar.com', False)
         vhost.add_host('test.me.org')
@@ -138,67 +132,67 @@ class VHostTest(unittest.TestCase):
         vhost.remove_host('foo.bar.com')
         self.assertEqual({'test.me.org': None}, vhost.get_host())
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       ['127.0.0.0/8', '192.168.0.0/16'],
                       {'test.me.org': None})
         self.assertEqual({'test.me.org': None}, vhost.get_host())
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                       host = {'test.me.org': None})
         self.assertEqual({'test.me.org': None}, vhost.get_host())
 
     def test_equality(self):
         vhost_0 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertEqual(vhost_0, vhost_1)
         vhost_1 = VHost('different', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 81, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test_vhost', 80, 'HTTPS', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/var/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/var/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG', type = 'combined'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG', type = 'combined'), 
Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG', type = 'combined'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG', type = 
'combined')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/24', '192.168.0.0/16'],
                         {'host.domain': None})
         self.assertNotEqual(vhost_0, vhost_1)
         vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
-                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        [Log('ACCESSLOG'), Log('WARNINGLOG')],
                         ['127.0.0.0/8', '192.168.0.0/16'],
                         {'host.domain': True})
         self.assertNotEqual(vhost_0, vhost_1)
@@ -220,7 +214,7 @@ class VHostTest(unittest.TestCase):
 </VHOST>'''
         vhost = VHost.from_string(text)
         self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
-                                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                                      [Log('ACCESSLOG'), Log('WARNINGLOG')],
                                       ['127.0.0.0/8', '192.168.0.0/16'],
                                       {'host.domain': True, 'test.domain': 
None}))
 
@@ -240,7 +234,7 @@ class VHostTest(unittest.TestCase):
 </VHOST>'''
         vhost = VHost.from_lxml_element(etree.XML(text))
         self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
-                                 Log('ACCESSLOG'), Log('WARNINGLOG'),
+                                 [Log('ACCESSLOG'), Log('WARNINGLOG')],
                                  ['127.0.0.0/8', '192.168.0.0/16'],
                                  {'host.domain': True, 'test.domain': None}))
 
@@ -297,11 +291,11 @@ class VHostTest(unittest.TestCase):
 class VHostsTest(unittest.TestCase):
     def setUp(self):
         self.vhost_0 = VHost('vhost 0', 80, 'HTTP', '/www', '/system',
-                             Log('ACCESSLOG'), Log('WARNINGLOG'),
+                             [Log('ACCESSLOG'), Log('WARNINGLOG')],
                              ['127.0.0.0/8', '192.168.0.0/16'],
                              {'host.domain': True, 'test.domain': None})
         self.vhost_1 = VHost('vhost 1', 443, 'HTTPS', '/www', '/system',
-                             Log('ACCESSLOG'), Log('WARNINGLOG'))
+                             [Log('ACCESSLOG'), Log('WARNINGLOG')])
         self.text = '<VHOSTS>{0}{1}</VHOSTS>'.format(str(self.vhost_0),
                                                      str(self.vhost_1))
 
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 568b902..cfa1cb0 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -20,19 +20,20 @@ from lxml import etree
 from log import Log
 
 class VHost():
-    def __init__(self, name, port, protocol, doc_root, sys_folder, access_log,
-                 warning_log, ip = [], host = {}):
-        '''Create new instance of VHost. access_log and warning_log are 
expected
-        to be instances of MyServer.pycontrollib.log.Log class. ip is expected
-        to be a collection and host is expected to be a dict {name: useRegex}
-        where None means not set.'''
+    def __init__(self, name = None, port = None, protocol = None,
+                 doc_root = None, sys_folder = None, logs = [],
+                 ip = [], host = {}):
+        '''Create new instance of VHost. logs and ip are expected to be a
+        collection and host is expected to be a dict {name: useRegex} where 
None
+        means not set.'''
         self.set_name(name)
         self.set_port(port)
         self.set_protocol(protocol)
         self.set_doc_root(doc_root)
         self.set_sys_folder(sys_folder)
-        self.set_access_log(access_log)
-        self.set_warning_log(warning_log)
+        self.logs = []
+        for log in logs:
+            self.add_log(log)
         self.ip = set()
         for ip_address in ip:
             self.add_ip(ip_address)
@@ -45,8 +46,7 @@ class VHost():
             self.port == other.port and self.protocol == other.protocol and \
             self.doc_root == other.doc_root and \
             self.sys_folder == other.sys_folder and \
-            self.access_log == other.access_log and \
-            self.warning_log == other.warning_log and self.ip == other.ip and \
+            self.logs == other.logs and self.ip == other.ip and \
             self.host == other.host
 
     def get_name(self):
@@ -55,8 +55,6 @@ class VHost():
 
     def set_name(self, name):
         '''Set VHost name.'''
-        if name is None:
-            raise AttributeError('name is required and can\'t be None')
         self.name = name
 
     def get_doc_root(self):
@@ -65,8 +63,6 @@ class VHost():
 
     def set_doc_root(self, doc_root):
         '''Set VHost doc root.'''
-        if doc_root is None:
-            raise AttributeError('doc_root is required and can\'t be None')
         self.doc_root = doc_root
 
     def get_sys_folder(self):
@@ -75,8 +71,6 @@ class VHost():
 
     def set_sys_folder(self, sys_folder):
         '''Set VHost sys folder.'''
-        if sys_folder is None:
-            raise AttributeError('sys_folder is required and can\'t be None')
         self.sys_folder = sys_folder
 
     def get_protocol(self):
@@ -85,8 +79,6 @@ class VHost():
 
     def set_protocol(self, protocol):
         '''Set VHost protocol.'''
-        if protocol is None:
-            raise AttributeError('protocol is required and can\'t be None')
         self.protocol = protocol
 
     def get_port(self):
@@ -96,36 +88,29 @@ class VHost():
     def set_port(self, port):
         '''Set VHost port.'''
         if port is None:
-            raise AttributeError('port is required and can\'t be None')
-        self.port = int(port)
-
-    def get_access_log(self):
-        '''Get VHost access log.'''
-        return self.access_log
-
-    def set_access_log(self, access_log):
-        '''Set VHost access log. access_log is expected to be an instance of
-        MyServer.pycontrollib.log.Log class.'''
-        if access_log is None:
-            raise AttributeError('access_log is required and can\'t be None')
-        if not isinstance(access_log, Log) or \
-                access_log.get_log_type() != 'ACCESSLOG':
-            raise AttributeError('given attribute is not an access log')
-        self.access_log = access_log
-
-    def get_warning_log(self):
-        '''Get VHost warning_log.'''
-        return self.warning_log
-
-    def set_warning_log(self, warning_log):
-        '''Set VHost warning_log. warning_log is expected to be an instance of
-        MyServer.pycontrollib.log.Log class'''
-        if warning_log is None:
-            raise AttributeError('warning_log is required and can\'t be None')
-        if not isinstance(warning_log, Log) or \
-                warning_log.get_log_type() != 'WARNINGLOG':
-            raise AttributeError('given attribute is not a warning log')
-        self.warning_log = warning_log
+            self.port = None
+        else:
+            self.port = int(port)
+
+    def get_logs(self):
+        '''Get list of logs.'''
+        return self.logs
+
+    def get_log(self, index):
+        '''Get index-th log.'''
+        return self.logs[index]
+
+    def remove_log(self, index):
+        '''Remove index-th log.'''
+        self.logs.pop(index)
+
+    def add_log(self, log, index = None):
+        '''Add log to VHost's logs, if index is None append it, otherwise 
insert
+        at index position.'''
+        if index is None:
+            self.logs.append(log)
+        else:
+            self.logs.insert(index, log)
 
     def get_ip(self):
         '''Get VHost ip set.'''
@@ -161,11 +146,16 @@ class VHost():
             element.text = text
             return element
         root = etree.Element('VHOST')
-        root.append(make_element('NAME', self.name))
-        root.append(make_element('PORT', str(self.port)))
-        root.append(make_element('PROTOCOL', self.protocol))
-        root.append(make_element('DOCROOT', self.doc_root))
-        root.append(make_element('SYSFOLDER', self.sys_folder))
+        if self.name is not None:
+            root.append(make_element('NAME', self.name))
+        if self.port is not None:
+            root.append(make_element('PORT', str(self.port)))
+        if self.protocol is not None:
+            root.append(make_element('PROTOCOL', self.protocol))
+        if self.doc_root is not None:
+            root.append(make_element('DOCROOT', self.doc_root))
+        if self.sys_folder is not None:
+            root.append(make_element('SYSFOLDER', self.sys_folder))
         for ip_address in self.ip:
             root.append(make_element('IP', ip_address))
         for host, use_regex in self.host.iteritems():
@@ -173,8 +163,8 @@ class VHost():
             if use_regex is not None:
                 element.set('useRegex', 'YES' if use_regex else 'NO')
             root.append(element)
-        root.append(self.access_log.to_lxml_element())
-        root.append(self.warning_log.to_lxml_element())
+        for log in self.logs:
+            root.append(log.to_lxml_element())
         return root
 
     @staticmethod
@@ -189,8 +179,7 @@ class VHost():
         sys_folder = None
         ip = []
         host = {}
-        access_log = None
-        warning_log = None
+        logs = []
         for child in list(root):
             if child.tag == 'NAME':
                 name = child.text
@@ -209,12 +198,9 @@ class VHost():
                 if use_regex is not None:
                     use_regex = use_regex == 'YES'
                 host[child.text] = use_regex
-            elif child.tag == 'ACCESSLOG':
-                access_log = Log.from_lxml_element(child)
-            elif child.tag == 'WARNINGLOG':
-                warning_log = Log.from_lxml_element(child)
-        return VHost(name, port, protocol, doc_root, sys_folder, access_log,
-                     warning_log, ip, host)
+            else:
+                logs.append(Log.from_lxml_element(child))
+        return VHost(name, port, protocol, doc_root, sys_folder, logs, ip, 
host)
     
     @staticmethod
     def from_string(text):



commit 04266076faf692115a2dbf46a8426530216b5f46
Author: Marek Aaron Sapota <address@hidden>
Date:   Thu Jul 9 17:04:32 2009 +0200

    Split mimetypes to/from string/lxml tests.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index d6f65e8..8b6fe05 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -20,20 +20,20 @@ from definition import Definition
 from lxml import etree
 
 class MIMEType():
-    def __init__(self, mime, handler, param = None, extension = [], path = 
None,
-                 filter = [], self_executed = None, definitions = []):
+    def __init__(self, mime, handler, param = None, extensions = [], path = 
None,
+                 filters = [], self_executed = None, definitions = []):
         '''Creates new MIMEType with specified attributes. extension, filter 
and
         definitions are expected to be iterable.'''
         self.set_mime(mime)
         self.set_handler(handler)
         self.set_param(param)
-        self.extension = set()
-        for single_extension in extension:
-            self.add_extension(single_extension)
+        self.extensions = set()
+        for extension in extensions:
+            self.add_extension(extension)
         self.set_path(path)
-        self.filter = []
-        for single_filter in filter:
-            self.add_filter(single_filter)
+        self.filters = []
+        for filter in filters:
+            self.add_filter(filter)
         self.set_self_executed(self_executed)
         self.definitions = []
         for definition in definitions:
@@ -69,15 +69,15 @@ class MIMEType():
 
     def get_extensions(self):
         '''Get associated extensions.'''
-        return self.extension
+        return self.extensions
 
     def remove_extension(self, extension):
         '''Remove extension from associated extensions.'''
-        self.extension.remove(extension)
+        self.extensions.remove(extension)
 
     def add_extension(self, extension):
         '''Add extension to associated extensions.'''
-        self.extension.add(extension)
+        self.extensions.add(extension)
 
     def get_path(self):
         '''Get associated path.'''
@@ -89,22 +89,22 @@ class MIMEType():
 
     def get_filters(self):
         '''Get associated filters.'''
-        return self.filter
+        return self.filters
 
     def get_filter(self, index):
         '''Get filter with given index.'''
-        return self.filter[index]
+        return self.filters[index]
 
     def remove_filter(self, index):
         '''Remove filter with given index.'''
-        self.filter.pop(index)
+        self.filters.pop(index)
 
     def add_filter(self, filter, index = None):
         '''Append filter after all other filters, or insert it at index.'''
         if index is None:
-            self.filter.append(filter)
+            self.filters.append(filter)
         else:
-            self.filter.insert(index, filter)
+            self.filters.insert(index, filter)
 
     def get_self_executed(self):
         '''Get self_executed setting.'''
@@ -139,9 +139,9 @@ class MIMEType():
             self.mime == other.mime and \
             self.handler == other.handler and \
             self.param == other.param and \
-            self.extension == other.extension and \
+            self.extensions == other.extensions and \
             self.path == other.path and \
-            self.filter == other.filter and \
+            self.filters == other.filters and \
             self.self_executed == other.self_executed and \
             self.definitions == other.definitions
 
@@ -164,9 +164,9 @@ class MIMEType():
             root.set('self', 'YES' if self.self_executed else 'NO')
         if self.path is not None:
             root.append(make_element('PATH', 'regex', self.path))
-        for element in map(make_extension_element, self.extension):
+        for element in map(make_extension_element, self.extensions):
             root.append(element)
-        for element in map(make_filter_element, self.filter):
+        for element in map(make_filter_element, self.filters):
             root.append(element)
         for definition in self.definitions:
             root.append(definition.to_lxml_element())
@@ -188,18 +188,18 @@ class MIMEType():
             self_executed = self_executed == 'YES'
         path = None
         extension = set()
-        filter = []
+        filters = []
         definitions = []
         for child in list(root):
             if child.tag == 'PATH':
                 path = child.get('regex')
             elif child.tag == 'FILTER':
-                filter.append(child.get('value'))
+                filters.append(child.get('value'))
             elif child.tag == 'EXTENSION':
                 extension.add(child.get('value'))
             elif child.tag == 'DEFINE':
                 definitions.append(Definition.from_lxml_element(child))
-        return MIMEType(mime, handler, param, extension, path, filter,
+        return MIMEType(mime, handler, param, extension, path, filters,
                         self_executed, definitions)
 
     @staticmethod
@@ -208,7 +208,7 @@ class MIMEType():
         return MIMEType.from_lxml_element(etree.XML(text))
 
 class MIMETypes():
-    def __init__(self, MIME_types):
+    def __init__(self, MIME_types = []):
         self.MIME_types = MIME_types
         
     def __eq__(self, other):
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 6f2a768..ba25a6b 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -19,31 +19,31 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 import unittest
 from lxml import etree
 from mimetypes import MIMEType, MIMETypes
-from definition import Definition
+from definition import DefinitionElement, DefinitionTree
 
 class MIMETypeTest(unittest.TestCase):
     def setUp(self):
         self.definitions = []
-        self.definitions.append(Definition.from_string(
-                '<DEFINE name="http.use_error_file" value="YES" />'))
-        self.definitions.append(Definition.from_string(
-                '<DEFINE name="http.error.file.404" value="404.html" />'))
-        self.definitions.append(Definition.from_string(
-                '''<DEFINE name="http.default_file">
-  <DEFINE value="index.html" />
-  <DEFINE value="index.htm" />
-</DEFINE>'''))
-        self.text = \
-            '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
-  <EXTENSION value="xhtml" />
-  <EXTENSION value="xml" />
-  <EXTENSION value="py" />
-  <FILTER value="gzip" />
-  <FILTER value="bzip2" />
-  {0}
-  <PATH regex="^/cgi-bin/python/.*$" />
-</MIME>'''.format('\n'.join(map(lambda element: str(element),
-                                self.definitions)))
+        self.definitions.append(DefinitionElement('http.use_error_file',
+                                                  {'value': 'YES'}))
+        self.definitions.append(DefinitionElement('http.error.file.404',
+                                                  {'value': '404.html'}))
+        self.definitions.append(
+            DefinitionTree(
+                'http.default_file',
+                [DefinitionElement(attributes = {'value': 'index.html'}),
+                 DefinitionElement(attributes = {'value': 'index.htm'})]))
+#        self.text = \
+#             '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
+#   <EXTENSION value="xhtml" />
+#   <EXTENSION value="xml" />
+#   <EXTENSION value="py" />
+#   <FILTER value="gzip" />
+#   <FILTER value="bzip2" />
+#   {0}
+#   <PATH regex="^/cgi-bin/python/.*$" />
+# </MIME>'''.format('\n'.join(map(lambda element: str(element),
+#                                 self.definitions)))
     def test_creation(self):
         MIMEType('text/plain', 'CGI')
         MIMEType('text/plain', 'CGI', '/usr/bin/python')
@@ -57,28 +57,6 @@ class MIMETypeTest(unittest.TestCase):
         MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
                  '^/cgi-bin/.*$', ['gzip'], False, self.definitions)
 
-    def test_from_string(self):
-        mime = MIMEType.from_string(self.text)
-        self.assertEqual(mime.get_mime(), 'application/xhtml+xml')
-        self.assertEqual(mime.get_handler(), 'CGI')
-        self.assertEqual(mime.get_param(), '/usr/bin/python')
-        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'py']))
-        self.assertEqual(mime.get_path(), '^/cgi-bin/python/.*$')
-        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2'])
-        self.assertEqual(mime.get_self_executed(), False)
-        self.assertEqual(mime.get_definitions(), self.definitions)
-
-    def test_from_lxml(self):
-        mime = MIMEType.from_lxml_element(etree.XML(self.text))
-        self.assertEqual(mime.get_mime(), 'application/xhtml+xml')
-        self.assertEqual(mime.get_handler(), 'CGI')
-        self.assertEqual(mime.get_param(), '/usr/bin/python')
-        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'py']))
-        self.assertEqual(mime.get_path(), '^/cgi-bin/python/.*$')
-        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2'])
-        self.assertEqual(mime.get_self_executed(), False)
-        self.assertEqual(mime.get_definitions(), self.definitions)
-
     def test_mime(self):
         mime = MIMEType('text/plain', 'SEND')
         self.assertEqual('text/plain', mime.get_mime())
@@ -113,7 +91,7 @@ class MIMETypeTest(unittest.TestCase):
         self.assertEqual(set(['py', 'html']), mime.get_extensions())
         mime.remove_extension('py')
         self.assertEqual(set(['html']), mime.get_extensions())
-        mime = MIMEType('text/plain', 'SEND', extension = set(['py', 'html']))
+        mime = MIMEType('text/plain', 'SEND', extensions = set(['py', 'html']))
         self.assertEqual(set(['py', 'html']), mime.get_extensions())
 
     def test_path(self):
@@ -126,7 +104,7 @@ class MIMETypeTest(unittest.TestCase):
         mime = MIMEType('text/plain', 'SEND', path = '^/www/.*$')
         self.assertEqual('^/www/.*$', mime.get_path())
 
-    def test_filter(self):
+    def test_filters(self):
         mime = MIMEType('text/plain', 'SEND')
         self.assertEqual([], mime.get_filters())
         mime.add_filter('gzip')
@@ -137,7 +115,7 @@ class MIMETypeTest(unittest.TestCase):
         mime.remove_filter(2)
         self.assertEqual(['bzip2', 'gzip'], mime.get_filters())
         self.assertEqual('gzip', mime.get_filter(1))
-        mime = MIMEType('text/plain', 'SEND', filter = ['gzip', 'bzip2'])
+        mime = MIMEType('text/plain', 'SEND', filters = ['gzip', 'bzip2'])
         self.assertEqual(['gzip', 'bzip2'], mime.get_filters())
 
     def test_self_executed(self):
@@ -164,64 +142,290 @@ class MIMETypeTest(unittest.TestCase):
         self.assertEqual(self.definitions, mime.get_definitions())
         
     def test_equality(self):
-        self.assertEqual(MIMEType.from_string(self.text),
-                         MIMEType.from_string(self.text))
-        self.assertNotEqual(MIMEType.from_string(self.text),
-                            MIMEType('text/plain', 'SEND'))
-        self.assertNotEqual(MIMEType.from_string(self.text), [])
+        self.assertEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  self.definitions),
+                         MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                  set(['py']), '^/.*$', ['gzip'], False,
+                                  self.definitions),
+                            MIMEType('other', 'CGI', '/usr/bin/python',
+                                  set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'SEND', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python26',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['pyc']), '^/.*$', ['gzip'], False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/other/.*$', ['gzip'], 
False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['bzip2'], False,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], True,
+                                     self.definitions))
+        self.assertNotEqual(MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     self.definitions),
+                            MIMEType('text/plain', 'CGI', '/usr/bin/python',
+                                     set(['py']), '^/.*$', ['gzip'], False,
+                                     []))
+        self.assertNotEqual(MIMEType('text/plain', 'SEND'), 'other type')
+
+    def test_from_string(self):
+        text = '<MIME mime="text/plain" handler="SEND" />'
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND')
+        self.assertEqual(mime, right)
+
+    def test_from_string_param(self):
+        text = '<MIME mime="text/plain" handler="SEND" param="/usr/bin/python" 
/>'
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', '/usr/bin/python')
+        self.assertEqual(mime, right)
+
+    def test_from_string_extension(self):
+        text = '<MIME mime="text/plain" handler="SEND"><EXTENSION value="py" 
/></MIME>'
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', extensions = ['py'])
+        self.assertEqual(mime, right)
+
+    def test_from_string_path(self):
+        text = '<MIME mime="text/plain" handler="SEND"><PATH regex="^/.*$" 
/></MIME>'
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', path = '^/.*$')
+        self.assertEqual(mime, right)
+
+    def test_from_string_filter(self):
+        text = '''<MIME mime="text/plain" handler="SEND">
+  <FILTER value="gzip" />
+  <FILTER value="bzip2" />
+</MIME>'''
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', filters = ['gzip', 'bzip2'])
+        self.assertEqual(mime, right)
+
+    def test_from_string_self_executed(self):
+        text = '<MIME mime="text/plain" handler="SEND" self="YES" />'
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', self_executed = True)
+        self.assertEqual(mime, right)
+
+    def test_from_string_definitions(self):
+        text = '<MIME mime="text/plain" handler="SEND">{0}</MIME>'.format(
+            '\n'.join(map(str, self.definitions)))
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', definitions = self.definitions)
+        self.assertEqual(mime, right)
+        
+    def test_from_string_full(self):
+        text = '''<MIME mime="text/plain" handler="SEND" self="YES" 
param="python">
+  <EXTENSION value="py" />
+  <FILTER value="gzip" />
+  <FILTER value="bzip2" />
+  <PATH regex="^/.*$" />
+  {0}
+</MIME>'''.format('\n'.join(map(str, self.definitions)))
+        mime = MIMEType.from_string(text)
+        right = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
+                         ['gzip', 'bzip2'], True, self.definitions)
+        self.assertEqual(mime, right)
+
+    def test_from_lxml(self):
+        text = '<MIME mime="text/plain" handler="SEND" />'
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND')
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_param(self):
+        text = '<MIME mime="text/plain" handler="SEND" param="/usr/bin/python" 
/>'
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', '/usr/bin/python')
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_extension(self):
+        text = '<MIME mime="text/plain" handler="SEND"><EXTENSION value="py" 
/></MIME>'
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', extensions = ['py'])
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_path(self):
+        text = '<MIME mime="text/plain" handler="SEND"><PATH regex="^/.*$" 
/></MIME>'
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', path = '^/.*$')
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_filter(self):
+        text = '''<MIME mime="text/plain" handler="SEND">
+  <FILTER value="gzip" />
+  <FILTER value="bzip2" />
+</MIME>'''
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', filters = ['gzip', 'bzip2'])
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_self_executed(self):
+        text = '<MIME mime="text/plain" handler="SEND" self="YES" />'
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', self_executed = True)
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_definitions(self):
+        text = '<MIME mime="text/plain" handler="SEND">{0}</MIME>'.format(
+            '\n'.join(map(str, self.definitions)))
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', definitions = self.definitions)
+        self.assertEqual(mime, right)
+
+    def test_from_lxml_full(self):
+        text = '''<MIME mime="text/plain" handler="SEND" self="YES" 
param="python">
+  <EXTENSION value="py" />
+  <FILTER value="gzip" />
+  <FILTER value="bzip2" />
+  <PATH regex="^/.*$" />
+  {0}
+</MIME>'''.format('\n'.join(map(str, self.definitions)))
+        mime = MIMEType.from_lxml_element(etree.XML(text))
+        right = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
+                         ['gzip', 'bzip2'], True, self.definitions)
+        self.assertEqual(mime, right)
+
+    def test_bad_root_tag(self):
+        text = '<ERROR mime="application/xhtml+xml" handler="CGI" />'
+        self.assertRaises(AttributeError, MIMEType.from_string, text)
+        self.assertRaises(AttributeError, MIMEType.from_lxml_element,
+                          etree.XML(text))
         
     def test_to_string(self):
-        mime = MIMEType.from_string(self.text)
+        mime = MIMEType('text/plain', 'SEND')
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_param(self):
+        mime = MIMEType('text/plain', 'SEND', '/usr/bin/python')
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_extension(self):
+        mime = MIMEType('text/plain', 'SEND', extensions = ['py'])
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_path(self):
+        mime = MIMEType('text/plain', 'SEND', path = '^/.*$')
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_filter(self):
+        mime = MIMEType('text/plain', 'SEND', filters = ['gzip', 'bzip2'])
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_self_executed(self):
+        mime = MIMEType('text/plain', 'SEND', self_executed = True)
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
+    def test_to_string_definitions(self):
+        mime = MIMEType('text/plain', 'SEND', definitions = self.definitions)
         copy = MIMEType.from_string(str(mime))
         self.assertEqual(mime, copy)
         
+    def test_to_string_full(self):
+        mime = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
+                        ['gzip', 'bzip2'], True, self.definitions)
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+
     def test_to_lxml(self):
-        mime = MIMEType.from_string(self.text)
+        mime = MIMEType('text/plain', 'SEND')
         copy = MIMEType.from_lxml_element(mime.to_lxml_element())
         self.assertEqual(mime, copy)
-        
 
-    def test_bad_root_tag(self):
-        text = '''<ERROR mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
-  <EXTENSION value="xhtml" />
-</ERROR>'''
-        self.assertRaises(AttributeError, MIMEType.from_string, text)
-        self.assertRaises(AttributeError, MIMEType.from_lxml_element,
-                          etree.XML(text))
+    def test_to_lxml_param(self):
+        mime = MIMEType('text/plain', 'SEND', '/usr/bin/python')
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
+    def test_to_lxml_extension(self):
+        mime = MIMEType('text/plain', 'SEND', extensions = ['py'])
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
+    def test_to_lxml_path(self):
+        mime = MIMEType('text/plain', 'SEND', path = '^/.*$')
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
+    def test_to_lxml_filter(self):
+        mime = MIMEType('text/plain', 'SEND', filters = ['gzip', 'bzip2'])
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
+    def test_to_lxml_self_executed(self):
+        mime = MIMEType('text/plain', 'SEND', self_executed = True)
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
+    def test_to_lxml_definitions(self):
+        mime = MIMEType('text/plain', 'SEND', definitions = self.definitions)
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
         
+    def test_to_lxml_full(self):
+        mime = MIMEType('text/plain', 'SEND', 'python', ['py'], '^/.*$',
+                        ['gzip', 'bzip2'], True, self.definitions)
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+
 class MIMETypesTest(unittest.TestCase):
     def setUp(self):
-        self.mime_0 = '''<MIME mime="text/html" handler="FASTCGI" self="YES" 
param="">
-  <EXTENSION value="fcgi"/>
-</MIME>'''
-        self.mime_1 = '''<MIME mime="text/plain" handler="SEND" param="">
-  <EXTENSION value="asc"/>
-  <EXTENSION value="c"/>
-  <EXTENSION value="cc"/>
-  <EXTENSION value="f"/>
-  <EXTENSION value="f90"/>
-  <EXTENSION value="h"/>
-  <EXTENSION value="hh"/>
-  <EXTENSION value="m"/>
-  <EXTENSION value="txt"/>
-</MIME>'''
+        self.mime_0 = MIMEType('text/html', 'FASTCGI', '', self_executed = 
True,
+                               extensions = ['fcgi'])
+        self.mime_1 = MIMEType('text/plain', 'SEND', '',
+                               extensions = ['asc', 'c', 'cc', 'f', 'f90', 
'h', 'hh'])
         self.text = '<MIMES>{0}{1}</MIMES>'.format(self.mime_0, self.mime_1)
     
     def test_creation(self):
-        mimes = MIMETypes([MIMEType.from_string(self.mime_0),
-                           MIMEType.from_string(self.mime_1)])
-        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
-        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
+        mimes = MIMETypes([self.mime_0, self.mime_1])
+        self.assertEqual(mimes.MIME_types[0], self.mime_0)
+        self.assertEqual(mimes.MIME_types[1], self.mime_1)
 
     def test_from_string(self):
         mimes = MIMETypes.from_string(self.text)
-        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
-        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
+        self.assertEqual(mimes.MIME_types[0], self.mime_0)
+        self.assertEqual(mimes.MIME_types[1], self.mime_1)
 
     def test_from_lxml(self):
         mimes = MIMETypes.from_lxml_element(etree.XML(self.text))
-        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
-        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
+        self.assertEqual(mimes.MIME_types[0], self.mime_0)
+        self.assertEqual(mimes.MIME_types[1], self.mime_1)
         
     def test_to_string(self):
         mimes = MIMETypes.from_string(self.text)



commit 46ad12574dae76f375e1fb29b1770176eb2036a4
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 8 20:53:29 2009 +0200

    Split log.py tests for easy testing.
    
    Removed non-empty location restriction from Stream class.

diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index ac9d052..c2824c9 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -19,21 +19,21 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 from lxml import etree
 
 class Stream():
-    def __init__(self, location, cycle = None, cycle_gzip = None, filter = []):
+    def __init__(self, location = None, cycle = None, cycle_gzip = None, 
filters = []):
         '''Create new Stream instance. filter is expected to be iterable.'''
         self.set_location(location)
         self.set_cycle(cycle)
         self.set_cycle_gzip(cycle_gzip)
-        self.filter = []
-        for single_filter in filter:
-            self.add_filter(single_filter)
+        self.filters = []
+        for filter in filters:
+            self.add_filter(filter)
 
     def __eq__(self, other):
         return isinstance(other, Stream) and \
             self.location == other.location and \
             self.cycle == other.cycle and \
             self.cycle_gzip == other.cycle_gzip and \
-            self.filter == other.filter
+            self.filters == other.filters
 
     def get_location(self):
         '''Get stream location.'''
@@ -41,8 +41,6 @@ class Stream():
 
     def set_location(self, location):
         '''Set stream location.'''
-        if location is None:
-            raise AttributeError('location is required and can\'t be None')
         self.location = location
 
     def get_cycle(self):
@@ -51,7 +49,10 @@ class Stream():
 
     def set_cycle(self, cycle):
         '''Set cycle value, None means not set.'''
-        self.cycle = cycle
+        if cycle is not None:
+            self.cycle = int(cycle)
+        else:
+            self.cycle = cycle
 
     def get_cycle_gzip(self):
         '''Get stream cycle_gzip value.'''
@@ -63,22 +64,22 @@ class Stream():
 
     def get_filters(self):
         '''Get list of stream filters.'''
-        return self.filter
+        return self.filters
 
     def get_filter(self, index):
         '''Get index-th filter.'''
-        return self.filter[index]
+        return self.filters[index]
 
     def add_filter(self, filter, index = None):
         '''Append a new filter, or insert it at index position.'''
         if index is None:
-            self.filter.append(filter)
+            self.filters.append(filter)
         else:
-            self.filter.insert(index, filter)
+            self.filters.insert(index, filter)
 
     def remove_filter(self, index):
         '''Remove index-th filter.'''
-        self.filter.pop(index)
+        self.filters.pop(index)
 
     @staticmethod
     def from_string(text):
@@ -95,11 +96,11 @@ class Stream():
         cycle_gzip = root.get('cycle_gzip', None)
         if cycle_gzip is not None:
             cycle_gzip = True if cycle_gzip == 'YES' else False
-        filter = []
+        filters = []
         for child in list(root):
             if child.tag == 'FILTER':
-                filter.append(child.text)
-        return Stream(location, cycle, cycle_gzip, filter)
+                filters.append(child.text)
+        return Stream(location, cycle, cycle_gzip, filters)
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
@@ -107,30 +108,31 @@ class Stream():
     def to_lxml_element(self):
         '''Convert to lxml.etree.Element.'''
         root = etree.Element('STREAM')
-        root.set('location', self.location)
+        if self.location is not None:
+            root.set('location', self.location)
         if self.cycle is not None:
-            root.set('cycle', self.cycle)
+            root.set('cycle', str(self.cycle))
         if self.cycle_gzip is not None:
             root.set('cycle_gzip', 'YES' if self.cycle_gzip else 'NO')
-        for filter in self.filter:
+        for filter in self.filters:
             element = etree.Element('FILTER')
             element.text = filter
             root.append(element)
         return root
 
 class Log():
-    def __init__(self, log_type, stream = [], type = None):
+    def __init__(self, log_type, streams = [], type = None):
         '''Create new Log instance. stream is expected to be iterable.'''
         self.set_log_type(log_type)
         self.set_type(type)
-        self.stream = []
-        for single_stream in stream:
-            self.add_stream(single_stream)
+        self.streams = []
+        for stream in streams:
+            self.add_stream(stream)
 
     def __eq__(self, other):
         return isinstance(other, Log) and \
             self.log_type == other.log_type and \
-            self.stream == other.stream and \
+            self.streams == other.streams and \
             self.type == other.type
     
     def get_type(self):
@@ -153,23 +155,23 @@ class Log():
 
     def get_streams(self):
         '''Get streams associated with this log.'''
-        return self.stream
+        return self.streams
 
     def add_stream(self, stream, index = None):
         '''Append stream to current streams or if index is not None insert
         stream ad index-th position.'''
         if index is None:
-            self.stream.append(stream)
+            self.streams.append(stream)
         else:
-            self.stream.insert(index, stream)
+            self.streams.insert(index, stream)
 
     def remove_stream(self, index):
         '''Remove stream from index-th position.'''
-        self.stream.pop(index)
+        self.streams.pop(index)
     
     def get_stream(self, index):
         '''Get index-th stream.'''
-        return self.stream[index]
+        return self.streams[index]
 
     @staticmethod
     def from_string(text):
@@ -181,11 +183,11 @@ class Log():
         '''Factory to produce log from lxml.etree.Element object.'''
         log_type = root.tag
         type = root.get('type', None)
-        stream = []
+        streams = []
         for child in list(root):
             if child.tag == 'STREAM':
-                stream.append(Stream.from_lxml_element(child))
-        return Log(log_type, stream, type)
+                streams.append(Stream.from_lxml_element(child))
+        return Log(log_type, streams, type)
 
     def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
@@ -195,6 +197,6 @@ class Log():
         root = etree.Element(self.log_type)
         if self.type is not None:
             root.set('type', self.type)
-        for stream in self.stream:
+        for stream in self.streams:
             root.append(stream.to_lxml_element())
         return root
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/log_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
index 55c86a0..9e01f57 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
@@ -22,21 +22,26 @@ from lxml import etree
 
 class StreamTest(unittest.TestCase):
     def test_creation(self):
+        log = Stream()
         log = Stream('/logs/MyServer.log')
         log = Stream('/logs/MyServer.log', 1048576)
         log = Stream('/logs/MyServer.log', 1048576, False)
         log = Stream('/logs/MyServer.log', 1048576, False, ['gzip', 'bzip2'])
 
     def test_location(self):
-        log = Stream('/logs/MyServer.log')
-        self.assertEqual('/logs/MyServer.log', log.get_location())
+        log = Stream()
+        self.assertEqual(None, log.get_location())
         log.set_location('/logs/new.log')
         self.assertEqual('/logs/new.log', log.get_location())
-        self.assertRaises(AttributeError, log.set_location, None)
-        self.assertRaises(AttributeError, Stream, None)
+        log.set_location(None)
+        self.assertEqual(None, log.get_location())
+        log = Stream('/logs/MyServer.log')
+        self.assertEqual('/logs/MyServer.log', log.get_location())
+        log = Stream(location = '/logs/MyServer.log')
+        self.assertEqual('/logs/MyServer.log', log.get_location())
 
     def test_cycle(self):
-        log = Stream('path')
+        log = Stream()
         self.assertEqual(None, log.get_cycle())
         log.set_cycle(123)
         self.assertEqual(123, log.get_cycle())
@@ -44,9 +49,11 @@ class StreamTest(unittest.TestCase):
         self.assertEqual(None, log.get_cycle())
         log = Stream('path', 123)
         self.assertEqual(123, log.get_cycle())
+        log = Stream(cycle = 123)
+        self.assertEqual(123, log.get_cycle())
 
     def test_cycle_gzip(self):
-        log = Stream('path')
+        log = Stream()
         self.assertEqual(None, log.get_cycle_gzip())
         log.set_cycle_gzip(True)
         self.assertEqual(True, log.get_cycle_gzip())
@@ -54,11 +61,11 @@ class StreamTest(unittest.TestCase):
         self.assertEqual(None, log.get_cycle_gzip())
         log = Stream('path', 123, False)
         self.assertEqual(False, log.get_cycle_gzip())
-        log = Stream('path', cycle_gzip = False)
+        log = Stream(cycle_gzip = False)
         self.assertEqual(False, log.get_cycle_gzip())
 
-    def test_filter(self):
-        log = Stream('path')
+    def test_filters(self):
+        log = Stream()
         self.assertEqual([], log.get_filters())
         log.add_filter('gzip')
         self.assertEqual(['gzip'], log.get_filters())
@@ -71,42 +78,86 @@ class StreamTest(unittest.TestCase):
         self.assertEqual(['bzip2', 'bzip2'], log.get_filters())
         log = Stream('path', 123, False, ['gzip'])
         self.assertEqual(['gzip'], log.get_filters())
-        log = Stream('path', filter = ['gzip'])
+        log = Stream(filters = ['gzip'])
         self.assertEqual(['gzip'], log.get_filters())
 
     def test_from_string(self):
+        text = '<STREAM />'
+        log = Stream.from_string(text)
+        right = Stream()
+        self.assertEqual(log, right)
+
+    def test_from_string_location(self):
+        text = '<STREAM location="path" />'
+        log = Stream.from_string(text)
+        right = Stream(location = 'path')
+        self.assertEqual(log, right)
+
+    def test_from_string_cycle(self):
+        text = '<STREAM cycle="123" />'
+        log = Stream.from_string(text)
+        right = Stream(cycle = 123)
+        self.assertEqual(log, right)
+
+    def test_from_string_cycle_gzip(self):
+        text = '<STREAM cycle_gzip="NO" />'
+        log = Stream.from_string(text)
+        right = Stream(cycle_gzip = False)
+        self.assertEqual(log, right)
+
+    def test_from_string_filters(self):
+        text = '<STREAM><FILTER>gzip</FILTER><FILTER>bzip2</FILTER></STREAM>'
+        log = Stream.from_string(text)
+        right = Stream(filters = ['gzip', 'bzip2'])
+        self.assertEqual(log, right)
+
+    def test_from_string_full(self):
         text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
         log = Stream.from_string(text)
-        self.assertEqual('path', log.get_location())
-        self.assertEqual('123', log.get_cycle())
-        self.assertFalse(log.get_cycle_gzip())
-        self.assertEqual(['gzip', 'bzip2'], log.get_filters())
-        text = '<STREAM location="path" />'
-        log = Stream.from_string(text)
-        self.assertEqual('path', log.get_location())
-        self.assertEqual(None, log.get_cycle())
-        self.assertEqual(None, log.get_cycle_gzip())
-        self.assertEqual([], log.get_filters())
+        right = Stream('path', 123, False, ['gzip', 'bzip2'])
+        self.assertEqual(log, right)
 
     def test_from_lxml(self):
+        text = '<STREAM />'
+        log = Stream.from_lxml_element(etree.XML(text))
+        right = Stream()
+        self.assertEqual(log, right)
+
+    def test_from_lxml_location(self):
+        text = '<STREAM location="path" />'
+        log = Stream.from_lxml_element(etree.XML(text))
+        right = Stream(location = 'path')
+        self.assertEqual(log, right)
+
+    def test_from_lxml_cycle(self):
+        text = '<STREAM cycle="123" />'
+        log = Stream.from_lxml_element(etree.XML(text))
+        right = Stream(cycle = 123)
+        self.assertEqual(log, right)
+
+    def test_from_lxml_cycle_gzip(self):
+        text = '<STREAM cycle_gzip="NO" />'
+        log = Stream.from_lxml_element(etree.XML(text))
+        right = Stream(cycle_gzip = False)
+        self.assertEqual(log, right)
+
+    def test_from_lxml_filters(self):
+        text = '<STREAM><FILTER>gzip</FILTER><FILTER>bzip2</FILTER></STREAM>'
+        log = Stream.from_lxml_element(etree.XML(text))
+        right = Stream(filters = ['gzip', 'bzip2'])
+        self.assertEqual(log, right)
+
+    def test_from_lxml_full(self):
         text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
         log = Stream.from_lxml_element(etree.XML(text))
-        self.assertEqual('path', log.get_location())
-        self.assertEqual('123', log.get_cycle())
-        self.assertFalse(log.get_cycle_gzip())
-        self.assertEqual(['gzip', 'bzip2'], log.get_filters())
-        text = '<STREAM location="path" />'
-        log = Stream.from_lxml_element(etree.XML(text))
-        self.assertEqual('path', log.get_location())
-        self.assertEqual(None, log.get_cycle())
-        self.assertEqual(None, log.get_cycle_gzip())
-        self.assertEqual([], log.get_filters())
+        right = Stream('path', 123, False, ['gzip', 'bzip2'])
+        self.assertEqual(log, right)
 
     def test_bad_root_tag(self):
         text = '<ERROR location="path" />'
@@ -123,34 +174,74 @@ class StreamTest(unittest.TestCase):
                             Stream('path', 123, False, ['gzip', 'bzip2']))
         self.assertNotEqual(Stream('path', 123, True, ['gzip', 'bzip2']),
                             Stream('path', 123, False, ['gzip', 'bzip2']))
-        self.assertNotEqual(Stream('path', 123, True, ['bzip2']),
+        self.assertNotEqual(Stream('path', 123, False, ['bzip2']),
                             Stream('path', 123, False, ['gzip', 'bzip2']))
         self.assertNotEqual([], Stream('path'))
 
     def test_to_string(self):
-        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
-  <FILTER>gzip</FILTER>
-  <FILTER>bzip2</FILTER>
-</STREAM>'''
-        log = Stream.from_string(text)
+        log = Stream()
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_location(self):
+        log = Stream(location = 'path')
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_cycle(self):
+        log = Stream(cycle = 123)
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_cycle_gzip(self):
+        log = Stream(cycle_gzip = False)
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_filters(self):
+        log = Stream(filters = ['gzip', 'bzip2'])
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_full(self):
+        log = Stream('path', 123, False, ['gzip', 'bzip2'])
         copy = Stream.from_string(str(log))
         self.assertEqual(log, copy)
 
     def test_to_lxml(self):
-        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
-  <FILTER>gzip</FILTER>
-  <FILTER>bzip2</FILTER>
-</STREAM>'''
-        log = Stream.from_string(text)
+        log = Stream()
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_location(self):
+        log = Stream(location = 'path')
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_cycle(self):
+        log = Stream(cycle = 123)
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_cycle_gzip(self):
+        log = Stream(cycle_gzip = False)
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_filters(self):
+        log = Stream(filters = ['gzip', 'bzip2'])
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_full(self):
+        log = Stream('path', 123, False, ['gzip', 'bzip2'])
         copy = Stream.from_lxml_element(log.to_lxml_element())
         self.assertEqual(log, copy)
 
 class LogTest(unittest.TestCase):
     def setUp(self):
-        text_0 = '<STREAM location="file://logs/MyServerHTTP.log" />'
-        self.stream_0 = Stream.from_string(text_0)
-        text_1 = '<STREAM location="console://stdout" />'
-        self.stream_1 = Stream.from_string(text_1)
+        self.stream_0 = Stream(location = 'file://logs/MyServerHTTP.log')
+        self.stream_1 = Stream(location = 'console://stdout', filters = 
['gzip'])
 
     def test_creation(self):
         log = Log('ACCESSLOG')
@@ -193,34 +284,52 @@ class LogTest(unittest.TestCase):
         self.assertEqual([self.stream_0], log.get_streams())
 
     def test_from_string(self):
-        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
-            self.stream_0, self.stream_1)
+        text = '<ACCESSLOG />'
         log = Log.from_string(text)
-        self.assertEqual('ACCESSLOG', log.get_log_type())
-        self.assertEqual('combined', log.get_type())
-        self.assertEqual([self.stream_0, self.stream_1], log.get_streams())
+        right = Log('ACCESSLOG')
+        self.assertEqual(log, right)
 
-    def test_from_lxml(self):
-        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
-            self.stream_0, self.stream_1)
-        log = Log.from_lxml_element(etree.XML(text))
-        self.assertEqual('ACCESSLOG', log.get_log_type())
-        self.assertEqual('combined', log.get_type())
-        self.assertEqual([self.stream_0, self.stream_1], log.get_streams())
+    def test_from_string_type(self):
+        text = '<ACCESSLOG type="combined" />'
+        log = Log.from_string(text)
+        right = Log('ACCESSLOG', type = "combined")
+        self.assertEqual(log, right)
 
-    def test_to_string(self):
-        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
-            self.stream_0, self.stream_1)
+    def test_from_string_streams(self):
+        text = '<ACCESSLOG>{0}{1}</ACCESSLOG>'.format(self.stream_0, 
self.stream_1)
         log = Log.from_string(text)
-        copy = Log.from_string(str(log))
-        self.assertEqual(log, copy)
+        right = Log('ACCESSLOG', streams = [self.stream_0, self.stream_1])
+        self.assertEqual(log, right)
 
-    def test_to_lxml(self):
-        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
-            self.stream_0, self.stream_1)
+    def test_from_string_full(self):
+        text = '<ACCESSLOG 
type="combined">{0}{1}</ACCESSLOG>'.format(self.stream_0, self.stream_1)
         log = Log.from_string(text)
-        copy = Log.from_lxml_element(log.to_lxml_element())
-        self.assertEqual(log, copy)
+        right = Log('ACCESSLOG', [self.stream_0, self.stream_1], 'combined')
+        self.assertEqual(log, right)
+
+    def test_from_lxml(self):
+        text = '<ACCESSLOG />'
+        log = Log.from_lxml_element(etree.XML(text))
+        right = Log('ACCESSLOG')
+        self.assertEqual(log, right)
+
+    def test_from_lxml_type(self):
+        text = '<ACCESSLOG type="combined" />'
+        log = Log.from_lxml_element(etree.XML(text))
+        right = Log('ACCESSLOG', type = "combined")
+        self.assertEqual(log, right)
+
+    def test_from_lxml_streams(self):
+        text = '<ACCESSLOG>{0}{1}</ACCESSLOG>'.format(self.stream_0, 
self.stream_1)
+        log = Log.from_lxml_element(etree.XML(text))
+        right = Log('ACCESSLOG', streams = [self.stream_0, self.stream_1])
+        self.assertEqual(log, right)
+
+    def test_from_lxml_full(self):
+        text = '<ACCESSLOG 
type="combined">{0}{1}</ACCESSLOG>'.format(self.stream_0, self.stream_1)
+        log = Log.from_lxml_element(etree.XML(text))
+        right = Log('ACCESSLOG', [self.stream_0, self.stream_1], 'combined')
+        self.assertEqual(log, right)
 
     def test_equality(self):
         self.assertEqual(Log('ACCESSLOG', [self.stream_0, self.stream_1], 
'combined'),
@@ -233,5 +342,45 @@ class LogTest(unittest.TestCase):
                             Log('WARNINGLOG', [self.stream_0], None))
         self.assertNotEqual([], Log('ACCESSLOG'))
 
+    def test_to_string(self):
+        log = Log('ACCESSLOG')
+        copy = Log.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_type(self):
+        log = Log('ACCESSLOG', type = "combined")
+        copy = Log.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_streams(self):
+        log = Log('ACCESSLOG', streams = [self.stream_0, self.stream_1])
+        copy = Log.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_string_full(self):
+        log = Log('ACCESSLOG', [self.stream_0, self.stream_1], 'combined')
+        copy = Log.from_string(str(log))
+        self.assertEqual(log, copy)
+        
+    def test_to_lxml(self):
+        log = Log('ACCESSLOG')
+        copy = Log.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_type(self):
+        log = Log('ACCESSLOG', type = "combined")
+        copy = Log.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_streams(self):
+        log = Log('ACCESSLOG', streams = [self.stream_0, self.stream_1])
+        copy = Log.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_to_lxml_full(self):
+        log = Log('ACCESSLOG', [self.stream_0, self.stream_1], 'combined')
+        copy = Log.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
 if __name__ == '__main__':
     unittest.main()



commit d3324c64a1aeac6cd067bc19437b7d7735dd44d2
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 8 19:13:00 2009 +0200

    Update and refactoring of definitions.
    
    - split tests for easy testing
    - definition.get_attribute generates KeyError if attribute doesn't exist
    - renamed values to definitions in DefinitionTree

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index 351b898..fd4c353 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -44,7 +44,7 @@ class Definition():
 
     def get_attribute(self, key):
         '''Get value of attribute key.'''
-        return self.attributes.get(key)
+        return self.attributes[key]
 
     def set_attribute(self, key, value):
         '''Set attribute key to given value.'''
@@ -122,38 +122,38 @@ class DefinitionElement(Definition):
 class DefinitionTree(Definition):
     '''Definition element containing other definitions.'''
 
-    def __init__(self, name = None, values = [], attributes = {}):
+    def __init__(self, name = None, definitions = [], attributes = {}):
         '''Creates new definition tree with given name, sub-definitions and
         attributes. values is expected to be iterable.'''
         Definition.__init__(self, name, attributes)
-        self.values = []
-        for value in values:
-            self.add_value(value)
+        self.definitions = []
+        for definition in definitions:
+            self.add_definition(definition)
 
     def __eq__(self, other):
         return isinstance(other, DefinitionTree) and \
             Definition.__eq__(self, other) and \
-            self.values == other.values
+            self.definitions == other.definitions
 
-    def get_values(self):
+    def get_definitions(self):
         '''Get all sub-definitions.'''
-        return self.values
+        return self.definitions
 
-    def get_value(self, index):
+    def get_definition(self, index):
         '''Get sub-definition with given index.'''
-        return self.values[index]
+        return self.definitions[index]
 
-    def add_value(self, value, index = None):
+    def add_definition(self, value, index = None):
         '''Add value to sub-definitions, either at given position, or at the
         end.'''
         if index is None:
-            self.values.append(value)
+            self.definitions.append(value)
         else:
-            self.values.insert(index, value)
+            self.definitions.insert(index, value)
 
-    def remove_value(self, index):
+    def remove_definition(self, index):
         '''Remove sub-definition with given index.'''
-        self.values.pop(index)
+        self.definitions.pop(index)
 
     @staticmethod
     def from_lxml_element(root):
@@ -162,8 +162,8 @@ class DefinitionTree(Definition):
             raise AttributeError('Expected DEFINE tag.')
         attributes = root.attrib
         name = attributes.pop('name', None)
-        values = map(Definition.from_lxml_element, list(root))
-        return DefinitionTree(name, values, attributes)
+        definitions = map(Definition.from_lxml_element, list(root))
+        return DefinitionTree(name, definitions, attributes)
 
     @staticmethod
     def from_string(text):
@@ -177,8 +177,8 @@ class DefinitionTree(Definition):
             root.set(key, value)
         if self.name is not None:
             root.set('name', self.name)
-        for value in self.values:
-            root.append(value.to_lxml_element())
+        for definition in self.definitions:
+            root.append(definition.to_lxml_element())
         return root
 
     def __str__(self):
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
index adf3533..6177b56 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
@@ -31,19 +31,26 @@ class DefinitionTest(unittest.TestCase):
         self.assertEqual('name1', definition.get_name())
         definition.set_name('name2')
         self.assertEqual('name2', definition.get_name())
+        definition.set_name(None)
+        self.assertEqual(None, definition.get_name())
+        definition = Definition(name = 'name1')
+        self.assertEqual('name1', definition.get_name())
 
     def test_attributes(self):
         definition = Definition('with attributes', {'a': 'b', 'c': 'd'})
         self.assertEqual('b', definition.get_attribute('a'))
         self.assertEqual('d', definition.get_attribute('c'))
-        self.assertEqual(None, definition.get_attribute('e'))
+        self.assertRaises(KeyError, definition.get_attribute, 'e')
         definition.set_attribute('e', 'f')
         self.assertEqual('f', definition.get_attribute('e'))
         definition.set_attribute('e', 'g')
         self.assertEqual('g', definition.get_attribute('e'))
         definition.remove_attribute('e')
-        self.assertEqual(None, definition.get_attribute('e'))
+        self.assertRaises(KeyError, definition.get_attribute, 'e')
         self.assertRaises(KeyError, definition.remove_attribute, 'e')
+        definition = Definition(attributes = {'a': 'b'})
+        self.assertEqual('b', definition.get_attribute('a'))
+        self.assertRaises(KeyError, definition.get_attribute, 'e')
 
     def test_value(self):
         self.assertRaises(KeyError, Definition, 'name', {'value': 'x'})
@@ -89,48 +96,60 @@ class DefinitionElementTest(unittest.TestCase):
         self.assertEqual('x', definition.get_attribute('value'))
         definition.set_attribute('value', 'y')
         self.assertEqual('y', definition.get_attribute('value'))
-        definition.remove_attribute('value')
+        definition.set_attribute('value', None)
         self.assertEqual(None, definition.get_attribute('value'))
+        definition.remove_attribute('value')
+        self.assertRaises(KeyError, definition.get_attribute, 'value')
+        definition = DefinitionElement()
+        self.assertRaises(KeyError, definition.get_attribute, 'value')
 
     def test_from_string(self):
-        text = '<DEFINE name="http.error.file.404" value="404.html" />'
+        text = '<DEFINE />'
         definition = DefinitionElement.from_string(text)
-        self.assertEqual('http.error.file.404', definition.get_name())
-        self.assertEqual('404.html', definition.get_attribute('value'))
-        text = '''<DEFINE server="/opt/bin/fastcgi_server" domain="fastcgi"
-host="localhost" port="2010" local="yes" uid="1000" gid="1000" />'''
+        right = DefinitionElement()
+        self.assertEqual(definition, right)
+
+    def test_from_string_name(self):
+        text = '<DEFINE name="test" />'
         definition = DefinitionElement.from_string(text)
-        self.assertEqual(None, definition.get_attribute('value'))
-        self.assertEqual(None, definition.get_name())
-        self.assertEqual('/opt/bin/fastcgi_server',
-                         definition.get_attribute('server'))
-        self.assertEqual('fastcgi', definition.get_attribute('domain'))
-        self.assertEqual('localhost', definition.get_attribute('host'))
-        self.assertEqual('2010', definition.get_attribute('port'))
-        self.assertEqual('yes', definition.get_attribute('local'))
-        self.assertEqual('1000', definition.get_attribute('uid'))
-        self.assertEqual('1000', definition.get_attribute('gid'))
+        right = DefinitionElement(name = "test")
+        self.assertEqual(definition, right)
 
+    def test_from_string_attributes(self):
+        text = '<DEFINE a="b" b="c" value="x" />'
+        definition = DefinitionElement.from_string(text)
+        right = DefinitionElement(attributes = {'a': 'b', 'b': 'c', 'value': 
'x'})
+        self.assertEqual(definition, right)
+
+    def test_from_string_full(self):
+        text = '<DEFINE name="test" value="x" a="b" />'
+        definition = DefinitionElement.from_string(text)
+        right = DefinitionElement('test', {'value': 'x', 'a': 'b'})
+        self.assertEqual(definition, right)
+        
     def test_from_lxml(self):
-        root = etree.Element('DEFINE')
-        root.set('server', '/opt/bin/fastcgi_server')
-        root.set('domain', 'fastcgi')
-        root.set('host', 'localhost')
-        root.set('port', '2010')
-        root.set('local', 'yes')
-        root.set('uid', '1000')
-        root.set('gid', '1000')
-        definition = DefinitionElement.from_lxml_element(root)
-        self.assertEqual(None, definition.get_attribute('value'))
-        self.assertEqual(None, definition.get_name())
-        self.assertEqual('/opt/bin/fastcgi_server',
-                         definition.get_attribute('server'))
-        self.assertEqual('fastcgi', definition.get_attribute('domain'))
-        self.assertEqual('localhost', definition.get_attribute('host'))
-        self.assertEqual('2010', definition.get_attribute('port'))
-        self.assertEqual('yes', definition.get_attribute('local'))
-        self.assertEqual('1000', definition.get_attribute('uid'))
-        self.assertEqual('1000', definition.get_attribute('gid'))
+        text = '<DEFINE />'
+        definition = DefinitionElement.from_lxml_element(etree.XML(text))
+        right = DefinitionElement()
+        self.assertEqual(definition, right)
+
+    def test_from_xml_name(self):
+        text = '<DEFINE name="test" />'
+        definition = DefinitionElement.from_lxml_element(etree.XML(text))
+        right = DefinitionElement(name = "test")
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_attributes(self):
+        text = '<DEFINE a="b" b="c" value="x" />'
+        definition = DefinitionElement.from_lxml_element(etree.XML(text))
+        right = DefinitionElement(attributes = {'a': 'b', 'b': 'c', 'value': 
'x'})
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_full(self):
+        text = '<DEFINE name="test" value="x" a="b" />'
+        definition = DefinitionElement.from_lxml_element(etree.XML(text))
+        right = DefinitionElement('test', {'value': 'x', 'a': 'b'})
+        self.assertEqual(definition, right)
 
     def test_equality(self):
         self.assertEqual(DefinitionElement('name', {'some': 'attributes'}),
@@ -142,20 +161,45 @@ host="localhost" port="2010" local="yes" uid="1000" 
gid="1000" />'''
         self.assertNotEqual(DefinitionElement(), 'different type')
 
     def test_to_string(self):
-        text = '''<DEFINE server="/opt/bin/fastcgi_server" domain="fastcgi"
-host="localhost" port="2010" local="yes" uid="1000" gid="1000" />'''
-        definition = DefinitionElement.from_string(text)
+        definition = DefinitionElement()
+        copy = DefinitionElement.from_string(str(definition))
+        self.assertEqual(definition, copy)
+    
+    def test_to_string_name(self):
+        definition = DefinitionElement(name = 'test')
+        copy = DefinitionElement.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_string_attributes(self):
+        definition = DefinitionElement(attributes = {'a': 'b', 'b': 'c', 
'value': 'x'})
+        copy = DefinitionElement.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_string_full(self):
+        definition = DefinitionElement('test', {'value': 'x', 'a': 'b'})
         copy = DefinitionElement.from_string(str(definition))
         self.assertEqual(definition, copy)
 
     def test_to_lxml(self):
-        text = '''<DEFINE name="test" server="/opt/bin/fastcgi_server"
-domain="fastcgi" host="localhost" port="2010" local="yes" uid="1000" gid="1000"
-/>'''
-        definition = DefinitionElement.from_string(text)
+        definition = DefinitionElement()
+        copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+    
+    def test_to_lxml_name(self):
+        definition = DefinitionElement(name = 'test')
+        copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+    def test_to_lxml_attributes(self):
+        definition = DefinitionElement(attributes = {'a': 'b', 'b': 'c', 
'value': 'x'})
         copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
 
+    def test_to_lxml_full(self):
+        definition = DefinitionElement('test', {'value': 'x', 'a': 'b'})
+        copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+    
     def test_bad_root_tag(self):
         text = '<ERROR name="http.error.file.404" value="404.html" />'
         self.assertRaises(AttributeError, Definition.from_string, text)
@@ -164,22 +208,17 @@ domain="fastcgi" host="localhost" port="2010" local="yes" 
uid="1000" gid="1000"
 
 class DefinitionTreeTest(unittest.TestCase):
     def setUp(self):
-        self.element_1 = DefinitionElement('http.error.file.404',
-                                           {'value': '404.html'})
-        self.element_2 = DefinitionElement(
-            attributes = {'server': '/opt/bin/fastcgi_server',
-                          'domain': 'fastcgi', 'host': 'localhost',
-                          'port': '2010', 'local': 'yes',
-                          'uid': '1000', 'gid': '1000'})
+        self.element_0 = DefinitionElement('test', {'value': 'x'})
+        self.element_1 = DefinitionElement(attributes = {'a': 'b', 'b': 'c'})
 
     def test_creation(self):
         definition = DefinitionTree()
         self.assertTrue(isinstance(definition, Definition))
         definition = DefinitionTree('name')
         self.assertTrue(isinstance(definition, Definition))
-        definition = DefinitionTree('name', [self.element_1])
+        definition = DefinitionTree('name', [self.element_0])
         self.assertTrue(isinstance(definition, Definition))
-        definition = DefinitionTree('name', [self.element_2], {'key': 'value'})
+        definition = DefinitionTree('name', [self.element_1], {'key': 'value'})
         self.assertTrue(isinstance(definition, Definition))
 
     def test_value(self):
@@ -187,73 +226,153 @@ class DefinitionTreeTest(unittest.TestCase):
         definition = Definition('name')
         self.assertRaises(KeyError, definition.set_attribute, 'value', 'x')
 
-    def test_values(self):
+    def test_definitions(self):
         definition = DefinitionTree()
-        self.assertEqual(0, len(definition.get_values()))
-        definition.add_value(self.element_1)
-        self.assertEqual(1, len(definition.get_values()))
-        self.assertEqual(self.element_1, definition.get_value(0))
-        definition.add_value(self.element_2)
-        self.assertEqual(2, len(definition.get_values()))
-        self.assertEqual(self.element_1, definition.get_value(0))
-        self.assertEqual(self.element_2, definition.get_value(1))
-        definition.remove_value(0)
-        self.assertEqual(1, len(definition.get_values()))
-        self.assertEqual(self.element_2, definition.get_value(0))
-        definition.add_value(self.element_1, 0)
-        self.assertEqual(2, len(definition.get_values()))
-        self.assertEqual(self.element_1, definition.get_value(0))
-        self.assertEqual(self.element_2, definition.get_value(1))
+        self.assertEqual(0, len(definition.get_definitions()))
+        definition.add_definition(self.element_0)
+        self.assertEqual(1, len(definition.get_definitions()))
+        self.assertEqual(self.element_0, definition.get_definition(0))
+        definition.add_definition(self.element_1)
+        self.assertEqual(2, len(definition.get_definitions()))
+        self.assertEqual(self.element_0, definition.get_definition(0))
+        self.assertEqual(self.element_1, definition.get_definition(1))
+        definition.remove_definition(0)
+        self.assertEqual(1, len(definition.get_definitions()))
+        self.assertEqual(self.element_1, definition.get_definition(0))
+        definition.add_definition(self.element_0, 0)
+        self.assertEqual(2, len(definition.get_definitions()))
+        self.assertEqual(self.element_0, definition.get_definition(0))
+        self.assertEqual(self.element_1, definition.get_definition(1))
+        self.assertRaises(IndexError, definition.get_definition, 2)
+        definition = DefinitionTree(definitions = [self.element_0, 
self.element_1])
+        self.assertEqual(2, len(definition.get_definitions()))
+        self.assertEqual(self.element_0, definition.get_definition(0))
+        self.assertEqual(self.element_1, definition.get_definition(1))
 
     def test_from_string(self):
-        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            self.element_1, self.element_2)
+        text = '<DEFINE />'
         definition = DefinitionTree.from_string(text)
-        self.assertEqual('test', definition.get_name())
-        self.assertEqual('value', definition.get_attribute('key'))
-        definitions = definition.get_values()
-        self.assertEqual(2, len(definitions))
-        self.assertEqual(self.element_1, definitions[0])
-        self.assertEqual(self.element_2, definitions[1])
+        right = DefinitionTree()
+        self.assertEqual(definition, right)
+
+    def test_from_string_name(self):
+        text = '<DEFINE name="test" />'
+        definition = DefinitionTree.from_string(text)
+        right = DefinitionTree(name = "test")
+        self.assertEqual(definition, right)
+
+    def test_from_string_definitions(self):
+        text = '<DEFINE>{0}{1}</DEFINE>'.format(self.element_0, self.element_1)
+        definition = DefinitionTree.from_string(text)
+        right = DefinitionTree(definitions = [self.element_0, self.element_1])
+        self.assertEqual(definition, right)
+
+    def test_from_string_attributes(self):
+        text = '<DEFINE a="b" b="c" />'
+        definition = DefinitionTree.from_string(text)
+        right = DefinitionTree(attributes = {'a': 'b', 'b': 'c'})
+        self.assertEqual(definition, right)
+
+    def test_from_string_full(self):
+        text = '<DEFINE name="test" a="b">{0}{1}</DEFINE>'.format(
+            self.element_0, self.element_1)
+        definition = DefinitionTree.from_string(text)
+        right = DefinitionTree('test', [self.element_0, self.element_1], {'a': 
'b'})
+        self.assertEqual(definition, right)
 
     def test_from_lxml(self):
-        root = etree.Element('DEFINE')
-        root.set('name', 'test')
-        root.set('key', 'value')
-        root.append(self.element_1.to_lxml_element())
-        root.append(self.element_2.to_lxml_element())
-        definition = DefinitionTree.from_lxml_element(root)
-        self.assertEqual('test', definition.get_name())
-        self.assertEqual('value', definition.get_attribute('key'))
-        definitions = definition.get_values()
-        self.assertEqual(2, len(definitions))
-        self.assertEqual(self.element_1, definitions[0])
-        self.assertEqual(self.element_2, definitions[1])
+        text = '<DEFINE />'
+        definition = DefinitionTree.from_lxml_element(etree.XML(text))
+        right = DefinitionTree()
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_name(self):
+        text = '<DEFINE name="test" />'
+        definition = DefinitionTree.from_lxml_element(etree.XML(text))
+        right = DefinitionTree(name = "test")
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_definitions(self):
+        text = '<DEFINE>{0}{1}</DEFINE>'.format(self.element_0, self.element_1)
+        definition = DefinitionTree.from_lxml_element(etree.XML(text))
+        right = DefinitionTree(definitions = [self.element_0, self.element_1])
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_attributes(self):
+        text = '<DEFINE a="b" b="c" />'
+        definition = DefinitionTree.from_lxml_element(etree.XML(text))
+        right = DefinitionTree(attributes = {'a': 'b', 'b': 'c'})
+        self.assertEqual(definition, right)
+
+    def test_from_lxml_full(self):
+        text = '<DEFINE name="test" a="b">{0}{1}</DEFINE>'.format(
+            self.element_0, self.element_1)
+        definition = DefinitionTree.from_lxml_element(etree.XML(text))
+        right = DefinitionTree('test', [self.element_0, self.element_1], {'a': 
'b'})
+        self.assertEqual(definition, right)
 
     def test_equality(self):
-        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            self.element_1, self.element_2)
-        self.assertEqual(DefinitionTree.from_string(text), 
DefinitionTree.from_string(text))
-        self.assertNotEqual(DefinitionTree.from_string(text), 
DefinitionTree('test'))
+        definition_0 = DefinitionTree('test', [self.element_0], {'a': 'b'})
+        definition_1 = DefinitionTree('test', [self.element_0], {'a': 'b'})
+        self.assertEqual(definition_0, definition_1)
+        self.assertNotEqual(definition_0, DefinitionTree('', [self.element_0], 
{'a': 'b'}))
+        self.assertNotEqual(definition_0, DefinitionTree('name', [], {'a': 
'b'}))
+        self.assertNotEqual(definition_0, DefinitionTree('name', 
[self.element_0], {'b': 'b'}))
         self.assertNotEqual(DefinitionTree(), 'different type')
 
     def test_to_string(self):
-        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            self.element_1, self.element_2)
-        definition = DefinitionTree.from_string(text)
+        definition = DefinitionTree()
+        copy = DefinitionTree.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_string_name(self):
+        definition = DefinitionTree(name = "test")
+        copy = DefinitionTree.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_top_string_definitions(self):
+        definition = DefinitionTree(definitions = [self.element_0, 
self.element_1])
         copy = DefinitionTree.from_string(str(definition))
         self.assertEqual(definition, copy)
 
+    def test_top_string_attributes(self):
+        definition = DefinitionTree(attributes = {'a': 'b', 'b': 'c'})
+        copy = DefinitionTree.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_string_full(self):
+        definition = DefinitionTree('test', [self.element_0, self.element_1], 
{'a': 'b'})
+        copy = DefinitionTree.from_string(str(definition))
+        self.assertEqual(definition, copy)
+        
     def test_to_lxml(self):
-        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            self.element_1, self.element_2)
-        definition = DefinitionTree.from_string(text)
+        definition = DefinitionTree()
+        copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+    def test_to_lxml_name(self):
+        definition = DefinitionTree(name = "test")
+        copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+    def test_top_lxml_definitions(self):
+        definition = DefinitionTree(definitions = [self.element_0, 
self.element_1])
+        copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+    def test_top_lxml_attributes(self):
+        definition = DefinitionTree(attributes = {'a': 'b', 'b': 'c'})
+        copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+    def test_to_lxml_full(self):
+        definition = DefinitionTree('test', [self.element_0, self.element_1], 
{'a': 'b'})
         copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
 
     def test_bad_root_tag(self):
         text = '<ERROR name="test" key="value">{0}{1}</ERROR>'.format(
-            self.element_1, self.element_2)
+            self.element_0, self.element_1)
         self.assertRaises(AttributeError, Definition.from_string, text)
         self.assertRaises(AttributeError, Definition.from_lxml_element,
                           etree.XML(text))



commit cbfd8d9703f7930d71aa05b01e5415187164cad8
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jul 7 22:04:59 2009 +0200

    Removed valid_* from pycontrollib for greater flexibility.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 031136b..d6f65e8 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -20,9 +20,6 @@ from definition import Definition
 from lxml import etree
 
 class MIMEType():
-    valid_handlers = set(['SEND', 'CGI', 'FASTCGI', 'SCGI', 'MSCGI', 'ISAPI',
-                          'WINCGI', 'PROXY'])
-
     def __init__(self, mime, handler, param = None, extension = [], path = 
None,
                  filter = [], self_executed = None, definitions = []):
         '''Creates new MIMEType with specified attributes. extension, filter 
and
@@ -58,9 +55,8 @@ class MIMEType():
 
     def set_handler(self, handler):
         '''Set associated handler.'''
-        if handler not in MIMEType.valid_handlers:
-            raise AttributeError(
-                '{0} is not a valid handler'.format(handler))
+        if handler is None:
+            raise AttributeError('handler is required and can\'t be None')
         self.handler = handler
 
     def get_param(self):
diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 7681af5..6f2a768 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -89,11 +89,11 @@ class MIMETypeTest(unittest.TestCase):
         
     def test_handler(self):
         mime = MIMEType('text/plain', 'SEND')
-        for handler in MIMEType.valid_handlers:
-            mime.set_handler(handler)
-            self.assertEqual(handler, mime.get_handler())
-        self.assertRaises(AttributeError, mime.set_handler, 'ERROR')
-        self.assertRaises(AttributeError, MIMEType, 'text/plain', 'ERROR')
+        self.assertEqual('SEND', mime.get_handler())
+        mime.set_handler('SOME HANDLER')
+        self.assertEqual('SOME HANDLER', mime.get_handler())
+        self.assertRaises(AttributeError, mime.set_handler, None)
+        self.assertRaises(AttributeError, MIMEType, 'text/plain', None)
 
     def test_param(self):
         mime = MIMEType('text/plain', 'SEND')
diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 0cbb673..b8979d3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -57,15 +57,13 @@ class VHostTest(unittest.TestCase):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual('HTTP', vhost.get_protocol())
-        for protocol in VHost.valid_protocols:
-            vhost.set_protocol(protocol)
-            self.assertEqual(protocol, vhost.get_protocol())
+        vhost.set_protocol('HTTPS')
+        self.assertEqual('HTTPS', vhost.get_protocol())
+        vhost.set_protocol('NEW PROTOCOL')
+        self.assertEqual('NEW PROTOCOL', vhost.get_protocol())
         self.assertRaises(AttributeError, vhost.set_protocol, None)
-        self.assertRaises(AttributeError, vhost.set_protocol, 'ERROR')
         self.assertRaises(AttributeError, VHost, 'test vhost', 80, None, 
'/www',
                           '/system', Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'ERROR',
-                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
 
     def test_doc_root(self):
         vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 0c8b0d3..568b902 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -20,9 +20,6 @@ from lxml import etree
 from log import Log
 
 class VHost():
-    valid_protocols = set(['HTTP', 'FTP', 'HTTPS', 'ISAPI', 'CGI', 'SCGI',
-                           'FastCGI', 'WinCGI', 'CONTROL'])
-    
     def __init__(self, name, port, protocol, doc_root, sys_folder, access_log,
                  warning_log, ip = [], host = {}):
         '''Create new instance of VHost. access_log and warning_log are 
expected
@@ -88,8 +85,6 @@ class VHost():
 
     def set_protocol(self, protocol):
         '''Set VHost protocol.'''
-        if protocol not in self.valid_protocols:
-            raise AttributeError('protocol is not valid')
         if protocol is None:
             raise AttributeError('protocol is required and can\'t be None')
         self.protocol = protocol



commit 0831902383662bdb633b4a3dbb53559dc9428dfc
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 23:24:35 2009 +0200

    Added instructions how to install pycontrol libraries.

diff --git a/misc/py_control_client/INSTALL b/misc/py_control_client/INSTALL
new file mode 100644
index 0000000..84a158d
--- /dev/null
+++ b/misc/py_control_client/INSTALL
@@ -0,0 +1,11 @@
+To install MyServer python control libraries run:
+
+$ python setup.py install
+
+or
+
+$ python setup.py install --prefix=/custom/path
+
+To generate tar.gz package with this libraries run:
+
+$ python setup.py sdist



commit 22a84c341826c11dbb6a89c6dbb113fbd855c264
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 23:17:37 2009 +0200

    Cleaned mimetypes constructors.

diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
index 4103343..031136b 100644
--- a/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
@@ -23,22 +23,24 @@ class MIMEType():
     valid_handlers = set(['SEND', 'CGI', 'FASTCGI', 'SCGI', 'MSCGI', 'ISAPI',
                           'WINCGI', 'PROXY'])
 
-    def __init__(self, mime, handler, param = None, extension = set(), path = 
None,
+    def __init__(self, mime, handler, param = None, extension = [], path = 
None,
                  filter = [], self_executed = None, definitions = []):
-        '''Creates new MIMEType with specified attributes.'''
-        self.mime = mime
-        if mime is None:
-            raise AttributeError('mime is required and can\'t be None')
-        if handler not in MIMEType.valid_handlers:
-            raise AttributeError(
-                '{0} is not a valid handler'.format(handler))
-        self.handler = handler
-        self.param = param
-        self.extension = extension
-        self.path = path
-        self.filter = filter
-        self.self_executed = self_executed
-        self.definitions = definitions
+        '''Creates new MIMEType with specified attributes. extension, filter 
and
+        definitions are expected to be iterable.'''
+        self.set_mime(mime)
+        self.set_handler(handler)
+        self.set_param(param)
+        self.extension = set()
+        for single_extension in extension:
+            self.add_extension(single_extension)
+        self.set_path(path)
+        self.filter = []
+        for single_filter in filter:
+            self.add_filter(single_filter)
+        self.set_self_executed(self_executed)
+        self.definitions = []
+        for definition in definitions:
+            self.add_definition(definition)
 
     def get_mime(self):
         '''Get associated mime type.'''
@@ -147,7 +149,8 @@ class MIMEType():
             self.self_executed == other.self_executed and \
             self.definitions == other.definitions
 
-    def to_lxml_element(self): 
+    def to_lxml_element(self):
+        '''Convert to lxml.etree.Element.'''
         def make_element(tag, attribute, value):
             element = etree.Element(tag)
             element.set(attribute, value)
@@ -178,7 +181,7 @@ class MIMEType():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce MIMEType from etree.Element object.'''
+        '''Factory to produce MIMEType from lxml.etree.Element object.'''
         if root.tag != 'MIME':
             raise AttributeError('Expected MIME tag.')
         mime = root.get('mime', None)
@@ -217,6 +220,7 @@ class MIMETypes():
             self.MIME_types == other.MIME_types
 
     def to_lxml_element(self):
+        '''Convert to lxml.etree.Element.'''
         root = etree.Element('MIMES')
         for mime in self.MIME_types:
             root.append(mime.to_lxml_element())
@@ -227,7 +231,7 @@ class MIMETypes():
         
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce MIMETypes from etree.Element object.'''
+        '''Factory to produce MIMETypes from lxml.etree.Element object.'''
         if root.tag != 'MIMES':
             raise AttributeError('Expected MIMES tag.')
         return MIMETypes(map(MIMEType.from_lxml_element, list(root)))



commit d77a6a2d6d3e12e0f4bc2cb107de0071dd06daf6
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 23:10:21 2009 +0200

    Cleaned up definition constructors.

diff --git a/misc/py_control_client/MyServer/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
index 1344723..351b898 100644
--- a/misc/py_control_client/MyServer/pycontrollib/definition.py
+++ b/misc/py_control_client/MyServer/pycontrollib/definition.py
@@ -24,10 +24,10 @@ class Definition():
 
     def __init__(self, name = None, attributes = {}):
         '''Creates new definition with given name and attributes.'''
-        if attributes.has_key('value'):
-            raise KeyError('value is not an allowed key')
-        self.name = name
-        self.attributes = attributes
+        self.set_name(name)
+        self.attributes = {}
+        for key, value in attributes.iteritems():
+            self.set_attribute(key, value)
 
     def __eq__(self, other):
         return isinstance(other, Definition) and \
@@ -49,7 +49,7 @@ class Definition():
     def set_attribute(self, key, value):
         '''Set attribute key to given value.'''
         if key == 'value':
-            raise KeyError('value is not an allowd key')
+            raise KeyError('value is not an allowed key')
         self.attributes[key] = value
 
     def remove_attribute(self, key):
@@ -58,7 +58,7 @@ class Definition():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce definition element or tree from etree.Element
+        '''Factory to produce definition element or tree from 
lxml.etree.Element
         object.'''
         if root.tag != 'DEFINE':
             raise AttributeError('Expected DEFINE tag.')
@@ -77,12 +77,9 @@ class DefinitionElement(Definition):
 
     def __init__(self, name = None, attributes = {}):
         '''Creates new definition element with given name and attributes.'''
-        if attributes.has_key('value'):
-            value = attributes.pop('value')
-            Definition.__init__(self, name, attributes)
-            self.attributes['value'] = value
-        else:
-            Definition.__init__(self, name, attributes)
+        Definition.__init__(self, name)
+        for key, value in attributes.iteritems():
+            self.set_attribute(key, value)
 
     def __eq__(self, other):
         return isinstance(other, DefinitionElement) and \
@@ -96,7 +93,7 @@ class DefinitionElement(Definition):
             Definition.set_attribute(self, key, value)
 
     def to_lxml_element(self):
-        '''Convert definition element to etree.Element object.'''
+        '''Convert definition element to lxml.etree.Element object.'''
         root = etree.Element('DEFINE')
         for key, value in self.attributes.iteritems():
             root.set(key, value)
@@ -109,7 +106,8 @@ class DefinitionElement(Definition):
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce definition element from etree.Element object.'''
+        '''Factory to produce definition element from lxml.etree.Element
+        object.'''
         if root.tag != 'DEFINE':
             raise AttributeError('Expected DEFINE tag.')
         attributes = root.attrib
@@ -126,9 +124,11 @@ class DefinitionTree(Definition):
 
     def __init__(self, name = None, values = [], attributes = {}):
         '''Creates new definition tree with given name, sub-definitions and
-        attributes.'''
+        attributes. values is expected to be iterable.'''
         Definition.__init__(self, name, attributes)
-        self.values = values
+        self.values = []
+        for value in values:
+            self.add_value(value)
 
     def __eq__(self, other):
         return isinstance(other, DefinitionTree) and \
@@ -157,7 +157,7 @@ class DefinitionTree(Definition):
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce definition tree from etree.Element object.'''
+        '''Factory to produce definition tree from lxml.etree.Element 
object.'''
         if root.tag != 'DEFINE':
             raise AttributeError('Expected DEFINE tag.')
         attributes = root.attrib
@@ -171,7 +171,7 @@ class DefinitionTree(Definition):
         return DefinitionTree.from_lxml_element(etree.XML(text))
 
     def to_lxml_element(self):
-        '''Convert definition tree to etree.Element object.'''
+        '''Convert definition tree to lxml.etree.Element object.'''
         root = etree.Element('DEFINE')
         for key, value in self.attributes.iteritems():
             root.set(key, value)



commit ed8d39eb36f7fe6a8800db2f7ddb26a389b5b693
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 23:02:07 2009 +0200

    Cleaned up log constructors.

diff --git a/misc/py_control_client/MyServer/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
index 44a65a0..ac9d052 100644
--- a/misc/py_control_client/MyServer/pycontrollib/log.py
+++ b/misc/py_control_client/MyServer/pycontrollib/log.py
@@ -20,12 +20,13 @@ from lxml import etree
 
 class Stream():
     def __init__(self, location, cycle = None, cycle_gzip = None, filter = []):
-        if location is None:
-            raise AttributeError('location is required and can\'t be None')
-        self.location = location
-        self.cycle = cycle
-        self.cycle_gzip = cycle_gzip
-        self.filter = filter
+        '''Create new Stream instance. filter is expected to be iterable.'''
+        self.set_location(location)
+        self.set_cycle(cycle)
+        self.set_cycle_gzip(cycle_gzip)
+        self.filter = []
+        for single_filter in filter:
+            self.add_filter(single_filter)
 
     def __eq__(self, other):
         return isinstance(other, Stream) and \
@@ -86,7 +87,7 @@ class Stream():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce stream from etree.Element object.'''
+        '''Factory to produce stream from lxml.etree.Element object.'''
         if root.tag != 'STREAM':
             raise AttributeError('Expected STREAM tag.')
         location = root.get('location')
@@ -104,6 +105,7 @@ class Stream():
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
     def to_lxml_element(self):
+        '''Convert to lxml.etree.Element.'''
         root = etree.Element('STREAM')
         root.set('location', self.location)
         if self.cycle is not None:
@@ -118,11 +120,12 @@ class Stream():
 
 class Log():
     def __init__(self, log_type, stream = [], type = None):
-        if log_type is None:
-            raise AttributeError('log_type is required and can\'t be None')
-        self.log_type = log_type
-        self.stream = stream
-        self.type = type
+        '''Create new Log instance. stream is expected to be iterable.'''
+        self.set_log_type(log_type)
+        self.set_type(type)
+        self.stream = []
+        for single_stream in stream:
+            self.add_stream(single_stream)
 
     def __eq__(self, other):
         return isinstance(other, Log) and \
@@ -175,7 +178,7 @@ class Log():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce log from etree.Element object.'''
+        '''Factory to produce log from lxml.etree.Element object.'''
         log_type = root.tag
         type = root.get('type', None)
         stream = []
@@ -188,6 +191,7 @@ class Log():
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
     def to_lxml_element(self):
+        '''Convert to lxml.etree.Element.'''
         root = etree.Element(self.log_type)
         if self.type is not None:
             root.set('type', self.type)



commit d12a997bfa7d627901ee9de2cf9095d9cd61f6d4
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 22:51:29 2009 +0200

    Updated method docstrings.

diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index a69a94e..0c8b0d3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -25,6 +25,10 @@ class VHost():
     
     def __init__(self, name, port, protocol, doc_root, sys_folder, access_log,
                  warning_log, ip = [], host = {}):
+        '''Create new instance of VHost. access_log and warning_log are 
expected
+        to be instances of MyServer.pycontrollib.log.Log class. ip is expected
+        to be a collection and host is expected to be a dict {name: useRegex}
+        where None means not set.'''
         self.set_name(name)
         self.set_port(port)
         self.set_protocol(protocol)
@@ -105,7 +109,8 @@ class VHost():
         return self.access_log
 
     def set_access_log(self, access_log):
-        '''Set VHost access log.'''
+        '''Set VHost access log. access_log is expected to be an instance of
+        MyServer.pycontrollib.log.Log class.'''
         if access_log is None:
             raise AttributeError('access_log is required and can\'t be None')
         if not isinstance(access_log, Log) or \
@@ -118,7 +123,8 @@ class VHost():
         return self.warning_log
 
     def set_warning_log(self, warning_log):
-        '''Set VHost warning_log.'''
+        '''Set VHost warning_log. warning_log is expected to be an instance of
+        MyServer.pycontrollib.log.Log class'''
         if warning_log is None:
             raise AttributeError('warning_log is required and can\'t be None')
         if not isinstance(warning_log, Log) or \
@@ -143,7 +149,7 @@ class VHost():
         return self.host
 
     def add_host(self, host, use_regex = None):
-        '''Add host to VHost dict.'''
+        '''Add host to VHost host dict.'''
         self.host[host] = use_regex
 
     def remove_host(self, host):
@@ -154,6 +160,7 @@ class VHost():
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
     def to_lxml_element(self):
+        '''Convert to instance of lxml.etree.Element.'''
         def make_element(tag, text):
             element = etree.Element(tag)
             element.text = text
@@ -177,7 +184,7 @@ class VHost():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce VHost from etree.Element object.'''
+        '''Factory to produce VHost from lxml.etree.Element object.'''
         if root.tag != 'VHOST':
             raise AttributeError('Expected VHOST tag.')
         name = None
@@ -221,12 +228,15 @@ class VHost():
 
 class VHosts():
     def __init__(self, VHosts):
+        '''Create a new instance of VHosts. VHosts attribute is expected to be 
a
+        list.'''
         self.VHosts = VHosts
 
     def __eq__(self, other):
         return isinstance(other, VHosts) and self.VHosts == other.VHosts
 
     def to_lxml_element(self):
+        '''Convert to lxml.etree.Element.'''
         root = etree.Element('VHOSTS')
         for vhost in self.VHosts:
             root.append(vhost.to_lxml_element())
@@ -237,7 +247,7 @@ class VHosts():
 
     @staticmethod
     def from_lxml_element(root):
-        '''Factory to produce VHosts from etree.Element object.'''
+        '''Factory to produce VHosts from lxml.etree.Element object.'''
         if root.tag != 'VHOSTS':
             raise AttributeError('Expected VHOSTS tag.')
         return VHosts(map(VHost.from_lxml_element, list(root)))



commit 9800f5ab48f188c8059d31655aa07d93af1c205b
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 22:40:09 2009 +0200

    Added control to valid protocols.

diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 9a8329a..a69a94e 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -21,7 +21,7 @@ from log import Log
 
 class VHost():
     valid_protocols = set(['HTTP', 'FTP', 'HTTPS', 'ISAPI', 'CGI', 'SCGI',
-                           'FastCGI', 'WinCGI'])
+                           'FastCGI', 'WinCGI', 'CONTROL'])
     
     def __init__(self, name, port, protocol, doc_root, sys_folder, access_log,
                  warning_log, ip = [], host = {}):



commit f8cb67b5f730bb0e7e4dde7e32ef62a4766ee25e
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 22:22:42 2009 +0200

    Added setup script for packaging and distribution of pycontrol.

diff --git a/misc/py_control_client/setup.py b/misc/py_control_client/setup.py
new file mode 100644
index 0000000..17415cd
--- /dev/null
+++ b/misc/py_control_client/setup.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from distutils.core import setup
+setup(name = 'MyServer', version = '0.0',
+      py_modules = ['MyServer.__init__'],
+      packages = ['MyServer/pycontrol', 'MyServer/pycontrollib'])



commit 104c01ca3fa9b00bb9c1c7904025e55475c32789
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 21:33:51 2009 +0200

    Added VHosts class.
    
    VHosts is ordered collection of VHost classes.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 7342f05..0cbb673 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -17,7 +17,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 import unittest
-from vhost import VHost
+from vhost import VHost, VHosts
 from log import Log
 from lxml import etree
 
@@ -296,5 +296,49 @@ class VHostTest(unittest.TestCase):
         self.assertRaises(AttributeError, VHost.from_lxml_element,
                           etree.XML(text))
 
+class VHostsTest(unittest.TestCase):
+    def setUp(self):
+        self.vhost_0 = VHost('vhost 0', 80, 'HTTP', '/www', '/system',
+                             Log('ACCESSLOG'), Log('WARNINGLOG'),
+                             ['127.0.0.0/8', '192.168.0.0/16'],
+                             {'host.domain': True, 'test.domain': None})
+        self.vhost_1 = VHost('vhost 1', 443, 'HTTPS', '/www', '/system',
+                             Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.text = '<VHOSTS>{0}{1}</VHOSTS>'.format(str(self.vhost_0),
+                                                     str(self.vhost_1))
+
+    def test_creation(self):
+        vhosts = VHosts([self.vhost_0, self.vhost_1])
+        self.assertEqual(vhosts.VHosts[0], self.vhost_0)
+        self.assertEqual(vhosts.VHosts[1], self.vhost_1)
+
+    def test_from_string(self):
+        vhosts = VHosts.from_string(self.text)
+        self.assertEqual(vhosts.VHosts[0], self.vhost_0)
+        self.assertEqual(vhosts.VHosts[1], self.vhost_1)
+
+    def test_from_string(self):
+        vhosts = VHosts.from_lxml_element(etree.XML(self.text))
+        self.assertEqual(vhosts.VHosts[0], self.vhost_0)
+        self.assertEqual(vhosts.VHosts[1], self.vhost_1)
+
+    def test_to_string(self):
+        vhosts = VHosts.from_string(self.text)
+        copy = VHosts.from_string(str(vhosts))
+        self.assertEqual(vhosts, copy)
+
+    def test_to_lxml(self):
+        vhosts = VHosts.from_string(self.text)
+        copy = VHosts.from_lxml_element(vhosts.to_lxml_element())
+        self.assertEqual(vhosts, copy)
+
+    def test_equality(self):
+        self.assertEqual(VHosts.from_string(self.text),
+                         VHosts.from_string(self.text))
+        self.assertNotEqual(VHosts.from_string(self.text),
+                            VHosts.from_string(
+                '<VHOSTS>{0}</VHOSTS>'.format(str(self.vhost_0))))
+        self.assertNotEqual(VHosts.from_string(self.text), [])
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index cc8adf3..9a8329a 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -218,3 +218,31 @@ class VHost():
     def from_string(text):
         '''Factory to produce VHost by parsing a string.'''
         return VHost.from_lxml_element(etree.XML(text))
+
+class VHosts():
+    def __init__(self, VHosts):
+        self.VHosts = VHosts
+
+    def __eq__(self, other):
+        return isinstance(other, VHosts) and self.VHosts == other.VHosts
+
+    def to_lxml_element(self):
+        root = etree.Element('VHOSTS')
+        for vhost in self.VHosts:
+            root.append(vhost.to_lxml_element())
+        return root
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce VHosts from etree.Element object.'''
+        if root.tag != 'VHOSTS':
+            raise AttributeError('Expected VHOSTS tag.')
+        return VHosts(map(VHost.from_lxml_element, list(root)))
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce VHosts from parsing a string.'''
+        return VHosts.from_lxml_element(etree.XML(text))



commit f07fbf3093aa9ac5617313795b7d4aae68ac73ce
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 21:22:41 2009 +0200

    Added constructor tests for mimetypes.

diff --git 
a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
index 011ba02..7681af5 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
@@ -207,6 +207,12 @@ class MIMETypesTest(unittest.TestCase):
 </MIME>'''
         self.text = '<MIMES>{0}{1}</MIMES>'.format(self.mime_0, self.mime_1)
     
+    def test_creation(self):
+        mimes = MIMETypes([MIMEType.from_string(self.mime_0),
+                           MIMEType.from_string(self.mime_1)])
+        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
+        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
+
     def test_from_string(self):
         mimes = MIMETypes.from_string(self.text)
         self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))



commit 4f880beec5e188d1386d04ab974f954ba50fd862
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 21:16:50 2009 +0200

    Changed Makefile rules to match test filenames.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
index e8e455b..d6b833d 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/Makefile
+++ b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
@@ -16,20 +16,20 @@
 PYTHON=/usr/bin/python
 PYTHONPATH:=$(shell dirname `pwd`):${PYTHONPATH}
 
-.PHONY: test test_log test_definition test_mimetypes test_vhost
+.PHONY: test log_test definition_test mimetypes_test vhost_test
 
-test: test_log test_definition test_mimetypes test_vhost
+test: log_test definition_test mimetypes_test vhost_test
 
-test_log:
+log_test:
        -${PYTHON} log_test.py
 
-test_definition:
+definition_test:
        -${PYTHON} definition_test.py
 
-test_mimetypes:
+mimetypes_test:
        -${PYTHON} mimetypes_test.py
 
-test_vhost:
+vhost_test:
        -${PYTHON} vhost_test.py
 
 .PHONY: clean



commit 5a1090eeb67304fea24bdf4d11edcec371e595b1
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jul 4 00:05:51 2009 +0200

    Added factory methods for VHost.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 1b35ebe..7342f05 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -206,5 +206,95 @@ class VHostTest(unittest.TestCase):
         self.assertNotEqual(vhost_0, vhost_1)
         self.assertNotEqual(vhost_0, [])
 
+    def test_from_string(self):
+        text = '''<VHOST>
+  <NAME>test vhost</NAME>
+  <PORT>80</PORT>
+  <IP>127.0.0.0/8</IP>
+  <IP>192.168.0.0/16</IP>
+  <PROTOCOL>HTTP</PROTOCOL>
+  <DOCROOT>/www</DOCROOT>
+  <SYSFOLDER>/system</SYSFOLDER>
+  <HOST useRegex="YES">host.domain</HOST>
+  <HOST>test.domain</HOST>
+  <ACCESSLOG />
+  <WARNINGLOG />
+</VHOST>'''
+        vhost = VHost.from_string(text)
+        self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
+                                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                                      ['127.0.0.0/8', '192.168.0.0/16'],
+                                      {'host.domain': True, 'test.domain': 
None}))
+
+    def test_from_lxml(self):
+        text = '''<VHOST>
+  <NAME>test vhost</NAME>
+  <PORT>80</PORT>
+  <IP>127.0.0.0/8</IP>
+  <IP>192.168.0.0/16</IP>
+  <PROTOCOL>HTTP</PROTOCOL>
+  <DOCROOT>/www</DOCROOT>
+  <SYSFOLDER>/system</SYSFOLDER>
+  <HOST useRegex="YES">host.domain</HOST>
+  <HOST>test.domain</HOST>
+  <ACCESSLOG />
+  <WARNINGLOG />
+</VHOST>'''
+        vhost = VHost.from_lxml_element(etree.XML(text))
+        self.assertEqual(vhost, VHost('test vhost', 80, 'HTTP', '/www', 
'/system',
+                                 Log('ACCESSLOG'), Log('WARNINGLOG'),
+                                 ['127.0.0.0/8', '192.168.0.0/16'],
+                                 {'host.domain': True, 'test.domain': None}))
+
+    def test_to_string(self):
+        text = '''<VHOST>
+  <NAME>test vhost</NAME>
+  <PORT>80</PORT>
+  <IP>127.0.0.0/8</IP>
+  <IP>192.168.0.0/16</IP>
+  <PROTOCOL>HTTP</PROTOCOL>
+  <DOCROOT>/www</DOCROOT>
+  <SYSFOLDER>/system</SYSFOLDER>
+  <HOST useRegex="YES">host.domain</HOST>
+  <HOST>test.domain</HOST>
+  <ACCESSLOG />
+  <WARNINGLOG />
+</VHOST>'''
+        vhost = VHost.from_string(text)
+        copy = VHost.from_string(str(vhost))
+        self.assertEqual(vhost, copy)
+
+    def test_to_lxml(self):
+        text = '''<VHOST>
+  <NAME>test vhost</NAME>
+  <PORT>80</PORT>
+  <IP>127.0.0.0/8</IP>
+  <IP>192.168.0.0/16</IP>
+  <PROTOCOL>HTTP</PROTOCOL>
+  <DOCROOT>/www</DOCROOT>
+  <SYSFOLDER>/system</SYSFOLDER>
+  <HOST useRegex="YES">host.domain</HOST>
+  <HOST>test.domain</HOST>
+  <ACCESSLOG />
+  <WARNINGLOG />
+</VHOST>'''
+        vhost = VHost.from_string(text)
+        copy = VHost.from_lxml_element(vhost.to_lxml_element())
+        self.assertEqual(vhost, copy)
+
+    def test_bad_root_tag(self):
+        text = '''<ERROR>
+  <NAME>test vhost</NAME>
+  <PORT>80</PORT>
+  <PROTOCOL>HTTP</PROTOCOL>
+  <DOCROOT>/www</DOCROOT>
+  <SYSFOLDER>/system</SYSFOLDER>
+  <ACCESSLOG />
+  <WARNINGLOG />
+</ERROR>'''
+        self.assertRaises(AttributeError, VHost.from_string, text)
+        self.assertRaises(AttributeError, VHost.from_lxml_element,
+                          etree.XML(text))
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 4375905..cc8adf3 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -98,7 +98,7 @@ class VHost():
         '''Set VHost port.'''
         if port is None:
             raise AttributeError('port is required and can\'t be None')
-        self.port = port
+        self.port = int(port)
 
     def get_access_log(self):
         '''Get VHost access log.'''
@@ -149,3 +149,72 @@ class VHost():
     def remove_host(self, host):
         '''Remove host from VHost host dict.'''
         self.host.pop(host)
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    def to_lxml_element(self):
+        def make_element(tag, text):
+            element = etree.Element(tag)
+            element.text = text
+            return element
+        root = etree.Element('VHOST')
+        root.append(make_element('NAME', self.name))
+        root.append(make_element('PORT', str(self.port)))
+        root.append(make_element('PROTOCOL', self.protocol))
+        root.append(make_element('DOCROOT', self.doc_root))
+        root.append(make_element('SYSFOLDER', self.sys_folder))
+        for ip_address in self.ip:
+            root.append(make_element('IP', ip_address))
+        for host, use_regex in self.host.iteritems():
+            element = make_element('HOST', host)
+            if use_regex is not None:
+                element.set('useRegex', 'YES' if use_regex else 'NO')
+            root.append(element)
+        root.append(self.access_log.to_lxml_element())
+        root.append(self.warning_log.to_lxml_element())
+        return root
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce VHost from etree.Element object.'''
+        if root.tag != 'VHOST':
+            raise AttributeError('Expected VHOST tag.')
+        name = None
+        port = None
+        protocol = None
+        doc_root = None
+        sys_folder = None
+        ip = []
+        host = {}
+        access_log = None
+        warning_log = None
+        for child in list(root):
+            if child.tag == 'NAME':
+                name = child.text
+            elif child.tag == 'PORT':
+                port = child.text
+            elif child.tag == 'PROTOCOL':
+                protocol = child.text
+            elif child.tag == 'DOCROOT':
+                doc_root = child.text
+            elif child.tag == 'SYSFOLDER':
+                sys_folder = child.text
+            elif child.tag == 'IP':
+                ip.append(child.text)
+            elif child.tag == 'HOST':
+                use_regex = child.get('useRegex', None)
+                if use_regex is not None:
+                    use_regex = use_regex == 'YES'
+                host[child.text] = use_regex
+            elif child.tag == 'ACCESSLOG':
+                access_log = Log.from_lxml_element(child)
+            elif child.tag == 'WARNINGLOG':
+                warning_log = Log.from_lxml_element(child)
+        return VHost(name, port, protocol, doc_root, sys_folder, access_log,
+                     warning_log, ip, host)
+    
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce VHost by parsing a string.'''
+        return VHost.from_lxml_element(etree.XML(text))



commit daf0d6ba253e47b2dd150519c1eb561672d38c0f
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 22:46:06 2009 +0200

    Extended VHost class.
    
    Still there are no factories for VHost.

diff --git a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
index 72b0702..1b35ebe 100644
--- a/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
+++ b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
@@ -17,68 +17,78 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 import unittest
-from vhost import Vhost
+from vhost import VHost
 from log import Log
 from lxml import etree
 
-class VhostTest(unittest.TestCase):
+class VHostTest(unittest.TestCase):
     def test_creation(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']))
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      ['127.0.0.0/8', '192.168.0.0/16'])
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']),
-                      set(['host.domain']))
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']),
-                      set(['host.domain']), True)
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'host.domain': None})
 
     def test_name(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual('test vhost', vhost.get_name())
         vhost.set_name('vhost')
         self.assertEqual('vhost', vhost.get_name())
         self.assertRaises(AttributeError, vhost.set_name, None)
+        self.assertRaises(AttributeError, VHost, None, 80, 'HTTP',
+                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
 
     def test_port(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual(80, vhost.get_port())
         vhost.set_port(8080)
         self.assertEqual(8080, vhost.get_port())
         self.assertRaises(AttributeError, vhost.set_port, None)
+        self.assertRaises(AttributeError, VHost, 'test vhost', None, 'HTTP',
+                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
 
     def test_protocol(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual('HTTP', vhost.get_protocol())
-        vhost.set_protocol('FTP')
-        self.assertEqual('FTP', vhost.get_protocol())
+        for protocol in VHost.valid_protocols:
+            vhost.set_protocol(protocol)
+            self.assertEqual(protocol, vhost.get_protocol())
         self.assertRaises(AttributeError, vhost.set_protocol, None)
+        self.assertRaises(AttributeError, vhost.set_protocol, 'ERROR')
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, None, 
'/www',
+                          '/system', Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'ERROR',
+                          '/www', '/system', Log('ACCESSLOG'), 
Log('WARNINGLOG'))
 
-    def test_docroot(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+    def test_doc_root(self):
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertEqual('/www', vhost.get_docroot())
-        vhost.set_docroot('/var/www')
-        self.assertEqual('/var/www', vhost.get_docroot())
-        self.assertRaises(AttributeError, vhost.set_docroot, None)
+        self.assertEqual('/www', vhost.get_doc_root())
+        vhost.set_doc_root('/var/www')
+        self.assertEqual('/var/www', vhost.get_doc_root())
+        self.assertRaises(AttributeError, vhost.set_doc_root, None)
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          None, '/system', Log('ACCESSLOG'), Log('WARNINGLOG'))
 
-    def test_sysfolder(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+    def test_sys_folder(self):
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertEqual('/system', vhost.get_sysfolder())
-        vhost.set_sysfolder('/var/system')
-        self.assertEqual('/var/system', vhost.get_sysfolder())
-        self.assertRaises(AttributeError, vhost.set_sysfolder, None)
+        self.assertEqual('/system', vhost.get_sys_folder())
+        vhost.set_sys_folder('/var/system')
+        self.assertEqual('/var/system', vhost.get_sys_folder())
+        self.assertRaises(AttributeError, vhost.set_sys_folder, None)
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          '/www', None, Log('ACCESSLOG'), Log('WARNINGLOG'))
 
     def test_log(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual(Log('ACCESSLOG'), vhost.get_access_log())
         self.assertEqual(Log('WARNINGLOG'), vhost.get_warning_log())
@@ -90,9 +100,19 @@ class VhostTest(unittest.TestCase):
                          vhost.get_warning_log())
         self.assertRaises(AttributeError, vhost.set_access_log, None)
         self.assertRaises(AttributeError, vhost.set_warning_log, None)
+        self.assertRaises(AttributeError, vhost.set_access_log, Log('ERROR'))
+        self.assertRaises(AttributeError, vhost.set_warning_log, Log('ERROR'))
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          '/www', '/system', None, Log('WARNINGLOG'))
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          '/www', '/system', Log('ACCESSLOG'), None)
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          '/www', '/system', Log('ERROR'), Log('WARNINGLOG'))
+        self.assertRaises(AttributeError, VHost, 'test vhost', 80, 'HTTP',
+                          '/www', '/system', Log('ACCESSLOG'), Log('ERROR'))
 
     def test_ip(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual(set(), vhost.get_ip())
         vhost.add_ip('10.0.0.0/8')
@@ -100,39 +120,91 @@ class VhostTest(unittest.TestCase):
         self.assertEqual(set(['10.0.0.0/8', '192.168.0.0/16']), vhost.get_ip())
         vhost.remove_ip('10.0.0.0/8')
         self.assertEqual(set(['192.168.0.0/16']), vhost.get_ip())
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      ['127.0.0.0/8', '192.168.0.0/16'])
+        self.assertEqual(set(['127.0.0.0/8', '192.168.0.0/16']), 
vhost.get_ip())
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']))
+                      ip = ['127.0.0.0/8', '192.168.0.0/16'])
         self.assertEqual(set(['127.0.0.0/8', '192.168.0.0/16']), 
vhost.get_ip())
 
     def test_host(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertEqual(set(), vhost.get_host())
-        vhost.add_host('foo.bar.com')
+        self.assertEqual({}, vhost.get_host())
+        vhost.add_host('foo.bar.com', False)
         vhost.add_host('test.me.org')
-        self.assertEqual(set(['foo.bar.com', 'test.me.org']), vhost.get_host())
+        self.assertEqual({'foo.bar.com': False, 'test.me.org': None},
+                         vhost.get_host())
         vhost.remove_host('foo.bar.com')
-        self.assertEqual(set(['test.me.org']), vhost.get_host())
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+        self.assertEqual({'test.me.org': None}, vhost.get_host())
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']),
-                      set(['test.me.org']))
-        self.assertEqual(set(['test.me.org']), vhost.get_host())
-
-    def test_host_use_regex(self):
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))
-        self.assertEqual(None, vhost.get_host_use_regex())
-        vhost.set_host_use_regex(True)
-        self.assertEqual(True, vhost.get_use_regex())
-        vhost.set_host_use_regex(None)
-        self.assertEqual(None, vhost.get_host_use_regex())
-        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      ['127.0.0.0/8', '192.168.0.0/16'],
+                      {'test.me.org': None})
+        self.assertEqual({'test.me.org': None}, vhost.get_host())
+        vhost = VHost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'),
-                      set(['127.0.0.0/8', '192.168.0.0/16']),
-                      set(['host.domain']), True)
-        self.assertEqual(True, vhost.get_use_regex())
+                      host = {'test.me.org': None})
+        self.assertEqual({'test.me.org': None}, vhost.get_host())
+
+    def test_equality(self):
+        vhost_0 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('different', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 81, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test_vhost', 80, 'HTTPS', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/var/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/var/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG', type = 'combined'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG', type = 'combined'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/24', '192.168.0.0/16'],
+                        {'host.domain': None})
+        self.assertNotEqual(vhost_0, vhost_1)
+        vhost_1 = VHost('test vhost', 80, 'HTTP', '/www', '/system',
+                        Log('ACCESSLOG'), Log('WARNINGLOG'),
+                        ['127.0.0.0/8', '192.168.0.0/16'],
+                        {'host.domain': True})
+        self.assertNotEqual(vhost_0, vhost_1)
+        self.assertNotEqual(vhost_0, [])
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
index 6d7af7a..4375905 100644
--- a/misc/py_control_client/MyServer/pycontrollib/vhost.py
+++ b/misc/py_control_client/MyServer/pycontrollib/vhost.py
@@ -17,20 +17,36 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 from lxml import etree
-
-class Vhost():
-    def __init__(self, name, port, protocol, docroot, sysfolder, access_log,
-                 warning_log, ip = set(), host = set(), host_use_regex = None):
-        self.name = name
-        self.port = port
-        self.protocol = protocol
-        self.docroot = docroot
-        self.sysfolder = sysfolder
-        self.access_log = access_log
-        self.warning_log = warning_log
-        self.ip = ip
-        self.host = host
-        self.host_use_regex = host_use_regex
+from log import Log
+
+class VHost():
+    valid_protocols = set(['HTTP', 'FTP', 'HTTPS', 'ISAPI', 'CGI', 'SCGI',
+                           'FastCGI', 'WinCGI'])
+    
+    def __init__(self, name, port, protocol, doc_root, sys_folder, access_log,
+                 warning_log, ip = [], host = {}):
+        self.set_name(name)
+        self.set_port(port)
+        self.set_protocol(protocol)
+        self.set_doc_root(doc_root)
+        self.set_sys_folder(sys_folder)
+        self.set_access_log(access_log)
+        self.set_warning_log(warning_log)
+        self.ip = set()
+        for ip_address in ip:
+            self.add_ip(ip_address)
+        self.host = {}
+        for single_host in host.iteritems():
+            self.add_host(single_host[0], single_host[1])
+
+    def __eq__(self, other):
+        return isinstance(other, VHost) and self.name == other.name and \
+            self.port == other.port and self.protocol == other.protocol and \
+            self.doc_root == other.doc_root and \
+            self.sys_folder == other.sys_folder and \
+            self.access_log == other.access_log and \
+            self.warning_log == other.warning_log and self.ip == other.ip and \
+            self.host == other.host
 
     def get_name(self):
         '''Get VHost name.'''
@@ -42,15 +58,25 @@ class Vhost():
             raise AttributeError('name is required and can\'t be None')
         self.name = name
 
-    def get_sysfolder(self):
-        '''Get VHost sysfolder.'''
-        return self.sysfolder
+    def get_doc_root(self):
+        '''Get VHost doc root.'''
+        return self.doc_root
 
-    def set_sysfolder(self, sysfolder):
-        '''Set VHost sysfolder.'''
-        if sysfolder is None:
-            raise AttributeError('sysfolder is required and can\'t be None')
-        self.sysfolder = sysfolder
+    def set_doc_root(self, doc_root):
+        '''Set VHost doc root.'''
+        if doc_root is None:
+            raise AttributeError('doc_root is required and can\'t be None')
+        self.doc_root = doc_root
+
+    def get_sys_folder(self):
+        '''Get VHost sys folder.'''
+        return self.sys_folder
+
+    def set_sys_folder(self, sys_folder):
+        '''Set VHost sys folder.'''
+        if sys_folder is None:
+            raise AttributeError('sys_folder is required and can\'t be None')
+        self.sys_folder = sys_folder
 
     def get_protocol(self):
         '''Get VHost protocol.'''
@@ -58,6 +84,8 @@ class Vhost():
 
     def set_protocol(self, protocol):
         '''Set VHost protocol.'''
+        if protocol not in self.valid_protocols:
+            raise AttributeError('protocol is not valid')
         if protocol is None:
             raise AttributeError('protocol is required and can\'t be None')
         self.protocol = protocol
@@ -80,6 +108,9 @@ class Vhost():
         '''Set VHost access log.'''
         if access_log is None:
             raise AttributeError('access_log is required and can\'t be None')
+        if not isinstance(access_log, Log) or \
+                access_log.get_log_type() != 'ACCESSLOG':
+            raise AttributeError('given attribute is not an access log')
         self.access_log = access_log
 
     def get_warning_log(self):
@@ -90,6 +121,9 @@ class Vhost():
         '''Set VHost warning_log.'''
         if warning_log is None:
             raise AttributeError('warning_log is required and can\'t be None')
+        if not isinstance(warning_log, Log) or \
+                warning_log.get_log_type() != 'WARNINGLOG':
+            raise AttributeError('given attribute is not a warning log')
         self.warning_log = warning_log
 
     def get_ip(self):
@@ -103,3 +137,15 @@ class Vhost():
     def remove_ip(self, ip):
         '''Remove ip from VHost ip set.'''
         self.ip.remove(ip)
+
+    def get_host(self):
+        '''Get VHost host dict.'''
+        return self.host
+
+    def add_host(self, host, use_regex = None):
+        '''Add host to VHost dict.'''
+        self.host[host] = use_regex
+
+    def remove_host(self, host):
+        '''Remove host from VHost host dict.'''
+        self.host.pop(host)



commit 5849c91c06ae47de0dac22178291e2e4e4eb4f2f
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 20:46:19 2009 +0200

    Removed old, unused code.

diff --git a/misc/py_control_client/MyServer/pycontrollib/configWrapper.py 
b/misc/py_control_client/MyServer/pycontrollib/configWrapper.py
deleted file mode 100644
index 63248e2..0000000
--- a/misc/py_control_client/MyServer/pycontrollib/configWrapper.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-'''
-MyServer
-Copyright (C) 2009 Free Software Foundation, Inc.
-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/>.
-'''
-
-from lxml import etree
-
-def make_vhost_list(data):
-    '''Make a list of VHostWrapper objects from data in format as
-    virtualhosts.xml configuration file.'''
-    parser = etree.XMLParser(remove_blank_text = True)
-    tree = etree.XML(data, parser)
-    return [VHostWrapper(host, from_text = False) for host in \
-        tree.findall('VHOST')]
-
-class VHostWrapper():
-    '''Class wrapping around vhost configuration.'''
-
-    allowed_protocols = set(['CONTROL', 'FTP', 'HTTP', 'HTTPS'])
-    
-    class ProtocolError(ValueError):
-        '''Exception raised when user tried to use invalid protocol.'''
-        def __init__(self, protocol):
-            '''protocol is the invalid protocol that user tried to use.'''
-            ValueError.__init__(self)
-            self.protocol = protocol
-        
-        def __str__(self):
-            return self.protocol + ' is not a valid protocol'
-
-    def __init__(self, data, from_text = True):
-        '''Create new VHostWrapper by parsing data, if from_text is False, then
-        it is assumed that data has already been parsed and is an lxml
-        object.'''
-        if from_text:
-            parser = etree.XMLParser(remove_blank_text = True)
-            self.tree = etree.XML(data, parser)
-        else:
-            self.tree = data
-        
-    def __str__(self):
-        return self.to_xml()
-
-    def to_xml(self):
-        '''Return configuration representation in xml format.'''
-        return etree.tostring(self.tree, encoding = 'utf-8',
-            xml_declaration = True, pretty_print = True)
-
-    def get_node(self, name, multiple = False):
-        '''Get value stored in node of given name. If multiple is True, returns
-        list of all values stored in nodes of given name'''
-        if not multiple:
-            node = self.tree.find(name)
-            if node is not None:
-                return node.text
-            return None
-        else:
-            nodes = self.tree.findal(name)
-            return map(lambda node: node.text, nodes)
-
-    def set_node(self, name, data):
-        '''Set value stored in node of given name to data. Don't use it when
-        multiple nodes with given name are present, use add_node and del_node
-        instead.'''
-        node = self.tree.find(name)
-        if data is None:
-            if node is not None:
-                self.tree.remove(node)
-            return
-        if node is not None:
-            node.text = data
-        else:
-            node = etree.Element(name)
-            node.text = data
-            self.tree.append(node)
-            
-    def add_node(self, name, data):
-        '''Add new node with given name and value to tree.'''
-        node = self.tree.makeelement(name)
-        node.text = data
-        self.tree.append(node)
-        
-    def del_node(self, name, data):
-        '''Remove node with given name and value from tree.'''
-        for node in self.tree.findall(name):
-            if node.text == data:
-                self.tree.remove(node)
-                break
-
-    def get_name(self):
-        '''Get vhost name.'''
-        return self.get_node('NAME')
-
-    def set_name(self, name):
-        '''Set vhost name.'''
-        self.set_node('NAME', name)
-
-    def get_port(self):
-        '''Get port used to accept connections.'''
-        return self.get_node('PORT')
-
-    def set_port(self, port):
-        '''Set port used to accept connections.'''
-        self.set_node('PORT', str(port))
-
-    def get_ip(self):
-        '''Get list of CIDR subnet masks for this vhost.'''
-        return self.get_node('IP', multiple = True)
-
-    def add_ip(self, ip):
-        '''Add CIDR subnet mask for this vhost.'''
-        self.add_node('IP', ip)
-        
-    def del_ip(self, ip):
-        '''Remove CIDR subnet mast for this vhost.'''
-        self.del_node('IP', ip)
-
-    def get_protocol(self):
-        '''Get used protocol.'''
-        return self.get_node('PROTOCOL')
-
-    def set_protocol(self, protocol):
-        '''Set used protocol. Will raise ProtocolError if there is no such
-        protocol.'''
-        if protocol not in self.allowed_protocols:
-            raise self.ProtocolError(protocol)
-        self.set_node('PROTOCOL', protocol)
-
-    def get_docroot(self):
-        '''Get document root directory.'''
-        return self.get_node('DOCROOT')
-
-    def set_docroot(self, docroot):
-        '''Set document root directory.'''
-        self.set_node('DOCROOT', docroot)
-
-    def get_sysfolder(self):
-        '''Get system directory.'''
-        return self.get_node('SYSFOLDER')
-
-    def set_sysfolder(self, sysfolder):
-        '''Set system directory.'''
-        self.set_node('SYSFOLDER', sysfolder)
-
-    def get_host(self): # TODO: useRegex option
-        '''Get list of host names.'''
-        return self.get_node('HOST', multiple = True)
-
-    def add_host(self, host): # TODO: useRegex option
-        '''Add host name.'''
-        self.add_node('HOST', host)
-
-    def del_host(self, host): # TODO: useRegex option
-        '''Remove host name.'''
-        self.del_node('HOST', host)
-
-    def get_accesslog(self): # TODO: streams, etc.
-        '''Get access log used by this vhost.'''
-        return self.get_node('ACCESSLOG')
-
-    def set_accesslog(self, accesslog): # TODO: streams, etc.
-        '''Set access log used by this vhost.'''
-        self.set_node('ACCESSLOG', accesslog)
-
-    def get_warninglog(self): # TODO: streams, etc.
-        '''Get errors log used by this vhost.'''
-        return self.get_node('WARNINGLOG')
-
-    def set_warninglog(self, warninglog): # TODO: streams, etc.
-        '''Set errors log used by this vhost.'''
-        self.set_node('WARNINGLOG', warninglog)
-



commit 7e19a0eb34f000df5d91c9ced6a955bca3a0f0df
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 20:43:10 2009 +0200

    Move tests to their own directory.

diff --git a/misc/py_control_client/MyServer/pycontrollib/__init__.py 
b/misc/py_control_client/MyServer/pycontrollib/__init__.py
index c671cf0..792d600 100644
--- a/misc/py_control_client/MyServer/pycontrollib/__init__.py
+++ b/misc/py_control_client/MyServer/pycontrollib/__init__.py
@@ -1,20 +1 @@
-# -*- coding: utf-8 -*-
-'''
-MyServer
-Copyright (C) 2009 Free Software Foundation, Inc.
-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/>.
-'''
-
-from pycontrollib import Controller
-import configWrapper
+#
diff --git a/misc/py_control_client/MyServer/pycontrollib/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
similarity index 95%
rename from misc/py_control_client/MyServer/pycontrollib/Makefile
rename to misc/py_control_client/MyServer/pycontrollib/test/Makefile
index 1c02cb4..e8e455b 100644
--- a/misc/py_control_client/MyServer/pycontrollib/Makefile
+++ b/misc/py_control_client/MyServer/pycontrollib/test/Makefile
@@ -14,6 +14,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 PYTHON=/usr/bin/python
+PYTHONPATH:=$(shell dirname `pwd`):${PYTHONPATH}
 
 .PHONY: test test_log test_definition test_mimetypes test_vhost
 
diff --git a/misc/py_control_client/MyServer/pycontrollib/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
similarity index 100%
rename from misc/py_control_client/MyServer/pycontrollib/definition_test.py
rename to misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
diff --git a/misc/py_control_client/MyServer/pycontrollib/log_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/log_test.py
similarity index 100%
rename from misc/py_control_client/MyServer/pycontrollib/log_test.py
rename to misc/py_control_client/MyServer/pycontrollib/test/log_test.py
diff --git a/misc/py_control_client/MyServer/pycontrollib/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
similarity index 100%
rename from misc/py_control_client/MyServer/pycontrollib/mimetypes_test.py
rename to misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
diff --git a/misc/py_control_client/MyServer/pycontrollib/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
similarity index 100%
rename from misc/py_control_client/MyServer/pycontrollib/vhost_test.py
rename to misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py



commit b8939650ccef039a9e38e4003a3ceadd9ee7ffb6
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 18:06:24 2009 +0200

    Moved pycontrol and pycontrollib to MyServer package.

diff --git a/misc/py_control_client/README 
b/misc/py_control_client/MyServer/README
similarity index 100%
rename from misc/py_control_client/README
rename to misc/py_control_client/MyServer/README
diff --git a/misc/py_control_client/__init__.py 
b/misc/py_control_client/MyServer/__init__.py
similarity index 100%
rename from misc/py_control_client/__init__.py
rename to misc/py_control_client/MyServer/__init__.py
diff --git a/misc/py_control_client/pycontrol/__init__.py 
b/misc/py_control_client/MyServer/pycontrol/__init__.py
similarity index 100%
rename from misc/py_control_client/pycontrol/__init__.py
rename to misc/py_control_client/MyServer/pycontrol/__init__.py
diff --git a/misc/py_control_client/pycontrol/pycontrol.py 
b/misc/py_control_client/MyServer/pycontrol/pycontrol.py
similarity index 100%
rename from misc/py_control_client/pycontrol/pycontrol.py
rename to misc/py_control_client/MyServer/pycontrol/pycontrol.py
diff --git a/misc/py_control_client/pycontrollib/Makefile 
b/misc/py_control_client/MyServer/pycontrollib/Makefile
similarity index 100%
rename from misc/py_control_client/pycontrollib/Makefile
rename to misc/py_control_client/MyServer/pycontrollib/Makefile
diff --git a/misc/py_control_client/pycontrollib/__init__.py 
b/misc/py_control_client/MyServer/pycontrollib/__init__.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/__init__.py
rename to misc/py_control_client/MyServer/pycontrollib/__init__.py
diff --git a/misc/py_control_client/pycontrollib/configWrapper.py 
b/misc/py_control_client/MyServer/pycontrollib/configWrapper.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/configWrapper.py
rename to misc/py_control_client/MyServer/pycontrollib/configWrapper.py
diff --git a/misc/py_control_client/pycontrollib/definition.py 
b/misc/py_control_client/MyServer/pycontrollib/definition.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/definition.py
rename to misc/py_control_client/MyServer/pycontrollib/definition.py
diff --git a/misc/py_control_client/pycontrollib/definition_test.py 
b/misc/py_control_client/MyServer/pycontrollib/definition_test.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/definition_test.py
rename to misc/py_control_client/MyServer/pycontrollib/definition_test.py
diff --git a/misc/py_control_client/pycontrollib/log.py 
b/misc/py_control_client/MyServer/pycontrollib/log.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/log.py
rename to misc/py_control_client/MyServer/pycontrollib/log.py
diff --git a/misc/py_control_client/pycontrollib/log_test.py 
b/misc/py_control_client/MyServer/pycontrollib/log_test.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/log_test.py
rename to misc/py_control_client/MyServer/pycontrollib/log_test.py
diff --git a/misc/py_control_client/pycontrollib/mimetypes.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/mimetypes.py
rename to misc/py_control_client/MyServer/pycontrollib/mimetypes.py
diff --git a/misc/py_control_client/pycontrollib/mimetypes_test.py 
b/misc/py_control_client/MyServer/pycontrollib/mimetypes_test.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/mimetypes_test.py
rename to misc/py_control_client/MyServer/pycontrollib/mimetypes_test.py
diff --git a/misc/py_control_client/pycontrollib/pycontrollib.py 
b/misc/py_control_client/MyServer/pycontrollib/pycontrollib.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/pycontrollib.py
rename to misc/py_control_client/MyServer/pycontrollib/pycontrollib.py
diff --git a/misc/py_control_client/pycontrollib/vhost.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/vhost.py
rename to misc/py_control_client/MyServer/pycontrollib/vhost.py
diff --git a/misc/py_control_client/pycontrollib/vhost_test.py 
b/misc/py_control_client/MyServer/pycontrollib/vhost_test.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/vhost_test.py
rename to misc/py_control_client/MyServer/pycontrollib/vhost_test.py
diff --git a/misc/py_control_client/sample.py 
b/misc/py_control_client/MyServer/sample.py
similarity index 100%
rename from misc/py_control_client/sample.py
rename to misc/py_control_client/MyServer/sample.py



commit 2376d719fa8b2ae79adb58f8f36ba2791f010ff1
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 18:03:26 2009 +0200

    Added clean action to Makefile.

diff --git a/misc/py_control_client/pycontrollib/Makefile 
b/misc/py_control_client/pycontrollib/Makefile
index 70d2d4c..1c02cb4 100644
--- a/misc/py_control_client/pycontrollib/Makefile
+++ b/misc/py_control_client/pycontrollib/Makefile
@@ -29,4 +29,8 @@ test_mimetypes:
        -${PYTHON} mimetypes_test.py
 
 test_vhost:
-       -${PYTHON} vhost_test.py
\ No newline at end of file
+       -${PYTHON} vhost_test.py
+
+.PHONY: clean
+clean:
+       rm *.py[co]



commit d428818d42962674193ea30b2992419d1e9aef3a
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 18:01:55 2009 +0200

    Partial vhost implementation.

diff --git a/misc/py_control_client/pycontrollib/vhost.py 
b/misc/py_control_client/pycontrollib/vhost.py
new file mode 100644
index 0000000..6d7af7a
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/vhost.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+class Vhost():
+    def __init__(self, name, port, protocol, docroot, sysfolder, access_log,
+                 warning_log, ip = set(), host = set(), host_use_regex = None):
+        self.name = name
+        self.port = port
+        self.protocol = protocol
+        self.docroot = docroot
+        self.sysfolder = sysfolder
+        self.access_log = access_log
+        self.warning_log = warning_log
+        self.ip = ip
+        self.host = host
+        self.host_use_regex = host_use_regex
+
+    def get_name(self):
+        '''Get VHost name.'''
+        return self.name
+
+    def set_name(self, name):
+        '''Set VHost name.'''
+        if name is None:
+            raise AttributeError('name is required and can\'t be None')
+        self.name = name
+
+    def get_sysfolder(self):
+        '''Get VHost sysfolder.'''
+        return self.sysfolder
+
+    def set_sysfolder(self, sysfolder):
+        '''Set VHost sysfolder.'''
+        if sysfolder is None:
+            raise AttributeError('sysfolder is required and can\'t be None')
+        self.sysfolder = sysfolder
+
+    def get_protocol(self):
+        '''Get VHost protocol.'''
+        return self.protocol
+
+    def set_protocol(self, protocol):
+        '''Set VHost protocol.'''
+        if protocol is None:
+            raise AttributeError('protocol is required and can\'t be None')
+        self.protocol = protocol
+
+    def get_port(self):
+        '''Get VHost port.'''
+        return self.port
+
+    def set_port(self, port):
+        '''Set VHost port.'''
+        if port is None:
+            raise AttributeError('port is required and can\'t be None')
+        self.port = port
+
+    def get_access_log(self):
+        '''Get VHost access log.'''
+        return self.access_log
+
+    def set_access_log(self, access_log):
+        '''Set VHost access log.'''
+        if access_log is None:
+            raise AttributeError('access_log is required and can\'t be None')
+        self.access_log = access_log
+
+    def get_warning_log(self):
+        '''Get VHost warning_log.'''
+        return self.warning_log
+
+    def set_warning_log(self, warning_log):
+        '''Set VHost warning_log.'''
+        if warning_log is None:
+            raise AttributeError('warning_log is required and can\'t be None')
+        self.warning_log = warning_log
+
+    def get_ip(self):
+        '''Get VHost ip set.'''
+        return self.ip
+
+    def add_ip(self, ip):
+        '''Add ip to VHost ip set.'''
+        self.ip.add(ip)
+
+    def remove_ip(self, ip):
+        '''Remove ip from VHost ip set.'''
+        self.ip.remove(ip)



commit 16aa7785a4d22f4b317e46408a3fff213f5ebe70
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 18:01:41 2009 +0200

    Makefile for running tests.

diff --git a/misc/py_control_client/pycontrollib/Makefile 
b/misc/py_control_client/pycontrollib/Makefile
new file mode 100644
index 0000000..70d2d4c
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/Makefile
@@ -0,0 +1,32 @@
+# MyServer
+# Copyright (C) 2009 Free Software Foundation, Inc.
+# 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/>.
+
+PYTHON=/usr/bin/python
+
+.PHONY: test test_log test_definition test_mimetypes test_vhost
+
+test: test_log test_definition test_mimetypes test_vhost
+
+test_log:
+       -${PYTHON} log_test.py
+
+test_definition:
+       -${PYTHON} definition_test.py
+
+test_mimetypes:
+       -${PYTHON} mimetypes_test.py
+
+test_vhost:
+       -${PYTHON} vhost_test.py
\ No newline at end of file



commit 61ac4a10693bea6f209bfda238d0a4787bbc6f0a
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 18:01:01 2009 +0200

    Updated tests for vhost.

diff --git a/misc/py_control_client/pycontrollib/vhost_test.py 
b/misc/py_control_client/pycontrollib/vhost_test.py
index 60333ce..72b0702 100644
--- a/misc/py_control_client/pycontrollib/vhost_test.py
+++ b/misc/py_control_client/pycontrollib/vhost_test.py
@@ -57,10 +57,26 @@ class VhostTest(unittest.TestCase):
         vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual('HTTP', vhost.get_protocol())
-        vhost.set_port('FTP')
+        vhost.set_protocol('FTP')
         self.assertEqual('FTP', vhost.get_protocol())
         self.assertRaises(AttributeError, vhost.set_protocol, None)
 
+    def test_docroot(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual('/www', vhost.get_docroot())
+        vhost.set_docroot('/var/www')
+        self.assertEqual('/var/www', vhost.get_docroot())
+        self.assertRaises(AttributeError, vhost.set_docroot, None)
+
+    def test_sysfolder(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual('/system', vhost.get_sysfolder())
+        vhost.set_sysfolder('/var/system')
+        self.assertEqual('/var/system', vhost.get_sysfolder())
+        self.assertRaises(AttributeError, vhost.set_sysfolder, None)
+
     def test_log(self):
         vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
                       Log('ACCESSLOG'), Log('WARNINGLOG'))
@@ -91,7 +107,7 @@ class VhostTest(unittest.TestCase):
 
     def test_host(self):
         vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))h
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual(set(), vhost.get_host())
         vhost.add_host('foo.bar.com')
         vhost.add_host('test.me.org')
@@ -106,7 +122,7 @@ class VhostTest(unittest.TestCase):
 
     def test_host_use_regex(self):
         vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
-                      Log('ACCESSLOG'), Log('WARNINGLOG'))h
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
         self.assertEqual(None, vhost.get_host_use_regex())
         vhost.set_host_use_regex(True)
         self.assertEqual(True, vhost.get_use_regex())
@@ -117,3 +133,6 @@ class VhostTest(unittest.TestCase):
                       set(['127.0.0.0/8', '192.168.0.0/16']),
                       set(['host.domain']), True)
         self.assertEqual(True, vhost.get_use_regex())
+
+if __name__ == '__main__':
+    unittest.main()



commit afe67f2f23bd7750d32095e70e14bb8708068af0
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 00:43:16 2009 +0200

    Fixed equality comparision between MIMETypes and other types.

diff --git a/misc/py_control_client/pycontrollib/mimetypes.py 
b/misc/py_control_client/pycontrollib/mimetypes.py
index a2102f9..4103343 100644
--- a/misc/py_control_client/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/pycontrollib/mimetypes.py
@@ -213,7 +213,8 @@ class MIMETypes():
         self.MIME_types = MIME_types
         
     def __eq__(self, other):
-        return self.MIME_types == other.MIME_types
+        return isinstance(other, MIMETypes) and \
+            self.MIME_types == other.MIME_types
 
     def to_lxml_element(self):
         root = etree.Element('MIMES')
diff --git a/misc/py_control_client/pycontrollib/mimetypes_test.py 
b/misc/py_control_client/pycontrollib/mimetypes_test.py
index 02db1fc..011ba02 100644
--- a/misc/py_control_client/pycontrollib/mimetypes_test.py
+++ b/misc/py_control_client/pycontrollib/mimetypes_test.py
@@ -233,6 +233,7 @@ class MIMETypesTest(unittest.TestCase):
         self.assertNotEqual(MIMETypes.from_string(self.text),
                             MIMETypes.from_string(
                 '<MIMES>{0}</MIMES>'.format(self.mime_0)))
+        self.assertNotEqual(MIMETypes.from_string(self.text), [])
 
     def test_bad_root_tag(self):
         text = '<ERROR>{0}{1}</ERROR>'.format(



commit 2cfed9a1f282424766351086df9ee5c6cdb9ff35
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jul 3 00:41:07 2009 +0200

    Extended tests for mimetypes.

diff --git a/misc/py_control_client/pycontrollib/mimetypes.py 
b/misc/py_control_client/pycontrollib/mimetypes.py
index a6a4190..a2102f9 100644
--- a/misc/py_control_client/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/pycontrollib/mimetypes.py
@@ -21,12 +21,14 @@ from lxml import etree
 
 class MIMEType():
     valid_handlers = set(['SEND', 'CGI', 'FASTCGI', 'SCGI', 'MSCGI', 'ISAPI',
-                          'WINCGI', 'PROxY'])
+                          'WINCGI', 'PROXY'])
 
     def __init__(self, mime, handler, param = None, extension = set(), path = 
None,
                  filter = [], self_executed = None, definitions = []):
         '''Creates new MIMEType with specified attributes.'''
         self.mime = mime
+        if mime is None:
+            raise AttributeError('mime is required and can\'t be None')
         if handler not in MIMEType.valid_handlers:
             raise AttributeError(
                 '{0} is not a valid handler'.format(handler))
@@ -44,6 +46,8 @@ class MIMEType():
 
     def set_mime(self, mime):
         '''Set associated mime type.'''
+        if mime is None:
+            raise AttributeError('mime is required and can\'t be None')
         self.mime = mime
 
     def get_handler(self):
@@ -133,7 +137,7 @@ class MIMEType():
         self.definitions.pop(index)
 
     def __eq__(self, other):
-        return \
+        return isinstance(other, MIMEType) and \
             self.mime == other.mime and \
             self.handler == other.handler and \
             self.param == other.param and \
diff --git a/misc/py_control_client/pycontrollib/mimetypes_test.py 
b/misc/py_control_client/pycontrollib/mimetypes_test.py
index 607e2cb..02db1fc 100644
--- a/misc/py_control_client/pycontrollib/mimetypes_test.py
+++ b/misc/py_control_client/pycontrollib/mimetypes_test.py
@@ -44,7 +44,19 @@ class MIMETypeTest(unittest.TestCase):
   <PATH regex="^/cgi-bin/python/.*$" />
 </MIME>'''.format('\n'.join(map(lambda element: str(element),
                                 self.definitions)))
-    
+    def test_creation(self):
+        MIMEType('text/plain', 'CGI')
+        MIMEType('text/plain', 'CGI', '/usr/bin/python')
+        MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']))
+        MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
+                 '^/cgi-bin/.*$')
+        MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
+                 '^/cgi-bin/.*$', ['gzip'])
+        MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
+                 '^/cgi-bin/.*$', ['gzip'], False)
+        MIMEType('text/plain', 'CGI', '/usr/bin/python', set(['py']),
+                 '^/cgi-bin/.*$', ['gzip'], False, self.definitions)
+
     def test_from_string(self):
         mime = MIMEType.from_string(self.text)
         self.assertEqual(mime.get_mime(), 'application/xhtml+xml')
@@ -67,53 +79,96 @@ class MIMETypeTest(unittest.TestCase):
         self.assertEqual(mime.get_self_executed(), False)
         self.assertEqual(mime.get_definitions(), self.definitions)
 
+    def test_mime(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual('text/plain', mime.get_mime())
+        mime.set_mime('application/xhtml+xml')
+        self.assertEqual('application/xhtml+xml', mime.get_mime())
+        self.assertRaises(AttributeError, mime.set_mime, None)
+        self.assertRaises(AttributeError, MIMEType, None, 'SEND')
+        
     def test_handler(self):
-        mime = MIMEType.from_string(self.text)
+        mime = MIMEType('text/plain', 'SEND')
         for handler in MIMEType.valid_handlers:
             mime.set_handler(handler)
+            self.assertEqual(handler, mime.get_handler())
         self.assertRaises(AttributeError, mime.set_handler, 'ERROR')
-        
-    def test_attributes(self):
-        mime = MIMEType.from_string(self.text)
-        mime.set_mime('plain/text')
-        self.assertEqual(mime.get_mime(), 'plain/text')
-        mime.set_handler('SEND')
-        self.assertEqual(mime.get_handler(), 'SEND')
-        mime.set_param('')
-        self.assertEqual(mime.get_param(), '')
+        self.assertRaises(AttributeError, MIMEType, 'text/plain', 'ERROR')
+
+    def test_param(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual(None, mime.get_param())
+        mime.set_param('/usr/bin/python')
+        self.assertEqual('/usr/bin/python', mime.get_param())
+        mime.set_param(None)
+        self.assertEqual(None, mime.get_param())
+        mime = MIMEType('text/plain', 'SEND', param = '/usr/bin/python')
+        self.assertEqual('/usr/bin/python', mime.get_param())
+    
+    def test_extension(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual(set(), mime.get_extensions())
+        mime.add_extension('py')
+        mime.add_extension('html')
+        self.assertEqual(set(['py', 'html']), mime.get_extensions())
         mime.remove_extension('py')
-        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml']))
-        mime.add_extension('pyc')
-        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'pyc']))
-        mime.set_path('^/python/')
-        self.assertEqual(mime.get_path(), '^/python/')
-        mime.remove_filter(0)
-        self.assertEqual(mime.get_filters(), ['bzip2'])
+        self.assertEqual(set(['html']), mime.get_extensions())
+        mime = MIMEType('text/plain', 'SEND', extension = set(['py', 'html']))
+        self.assertEqual(set(['py', 'html']), mime.get_extensions())
+
+    def test_path(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual(None, mime.get_path())
+        mime.set_path('^/www/.*$')
+        self.assertEqual('^/www/.*$', mime.get_path())
+        mime.set_path(None)
+        self.assertEqual(None, mime.get_path())
+        mime = MIMEType('text/plain', 'SEND', path = '^/www/.*$')
+        self.assertEqual('^/www/.*$', mime.get_path())
+
+    def test_filter(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual([], mime.get_filters())
         mime.add_filter('gzip')
-        self.assertEqual(mime.get_filters(), ['bzip2', 'gzip'])
-        mime.add_filter('gzip', 0)
-        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2', 'gzip'])
-        self.assertEqual(mime.get_filter(1), 'bzip2')
+        mime.add_filter('bzip2')
+        self.assertEqual(['gzip', 'bzip2'], mime.get_filters())
+        mime.add_filter('bzip2', 0)
+        self.assertEqual(['bzip2', 'gzip', 'bzip2'], mime.get_filters())
+        mime.remove_filter(2)
+        self.assertEqual(['bzip2', 'gzip'], mime.get_filters())
+        self.assertEqual('gzip', mime.get_filter(1))
+        mime = MIMEType('text/plain', 'SEND', filter = ['gzip', 'bzip2'])
+        self.assertEqual(['gzip', 'bzip2'], mime.get_filters())
+
+    def test_self_executed(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual(None, mime.get_self_executed())
         mime.set_self_executed(True)
-        self.assertEqual(mime.get_self_executed(), True)
-        definition = Definition.from_string(
-            '<DEFINE name="http.error.file.404" value="404.html" />')
-        mime.add_definition(definition)
-        self.definitions.append(definition)
-        self.assertEqual(mime.get_definitions(), self.definitions)
-        mime.add_definition(definition, 0)
-        self.definitions.insert(0, definition)
-        self.assertEqual(mime.get_definitions(), self.definitions)
-        mime.remove_definition(1)
-        self.definitions.pop(1)
-        self.assertEqual(mime.get_definitions(), self.definitions)
-        self.assertEqual(mime.get_definition(1), self.definitions[1])
+        self.assertEqual(True, mime.get_self_executed())
+        mime.set_self_executed(None)
+        self.assertEqual(None, mime.get_self_executed())
+        mime = MIMEType('text/plain', 'SEND', self_executed = True)
+        self.assertEqual(True, mime.get_self_executed())
+        
+    def test_definitions(self):
+        mime = MIMEType('text/plain', 'SEND')
+        self.assertEqual([], mime.get_definitions())
+        for definition in self.definitions:
+            mime.add_definition(definition)
+        self.assertEqual(self.definitions, mime.get_definitions())
+        for i in xrange(len(self.definitions)):
+            self.assertEqual(self.definitions[i], mime.get_definition(i))
+        mime.remove_definition(0)
+        self.assertEqual(self.definitions[1:], mime.get_definitions())
+        mime = MIMEType('text/plain', 'SEND', definitions = self.definitions)
+        self.assertEqual(self.definitions, mime.get_definitions())
         
     def test_equality(self):
         self.assertEqual(MIMEType.from_string(self.text),
                          MIMEType.from_string(self.text))
         self.assertNotEqual(MIMEType.from_string(self.text),
                             MIMEType('text/plain', 'SEND'))
+        self.assertNotEqual(MIMEType.from_string(self.text), [])
         
     def test_to_string(self):
         mime = MIMEType.from_string(self.text)



commit c66b92cc63c15a55abae354e818bcab4086f2d61
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 1 23:34:32 2009 +0200

    Basic vhost tests.

diff --git a/misc/py_control_client/pycontrollib/vhost_test.py 
b/misc/py_control_client/pycontrollib/vhost_test.py
new file mode 100644
index 0000000..60333ce
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/vhost_test.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 unittest
+from vhost import Vhost
+from log import Log
+from lxml import etree
+
+class VhostTest(unittest.TestCase):
+    def test_creation(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']))
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']),
+                      set(['host.domain']))
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']),
+                      set(['host.domain']), True)
+
+    def test_name(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual('test vhost', vhost.get_name())
+        vhost.set_name('vhost')
+        self.assertEqual('vhost', vhost.get_name())
+        self.assertRaises(AttributeError, vhost.set_name, None)
+
+    def test_port(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual(80, vhost.get_port())
+        vhost.set_port(8080)
+        self.assertEqual(8080, vhost.get_port())
+        self.assertRaises(AttributeError, vhost.set_port, None)
+
+    def test_protocol(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual('HTTP', vhost.get_protocol())
+        vhost.set_port('FTP')
+        self.assertEqual('FTP', vhost.get_protocol())
+        self.assertRaises(AttributeError, vhost.set_protocol, None)
+
+    def test_log(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual(Log('ACCESSLOG'), vhost.get_access_log())
+        self.assertEqual(Log('WARNINGLOG'), vhost.get_warning_log())
+        vhost.set_access_log(Log('ACCESSLOG', type = 'combined'))
+        vhost.set_warning_log(Log('WARNINGLOG', type = 'combined'))
+        self.assertEqual(Log('ACCESSLOG', type = 'combined'),
+                         vhost.get_access_log())
+        self.assertEqual(Log('WARNINGLOG', type = 'combined'),
+                         vhost.get_warning_log())
+        self.assertRaises(AttributeError, vhost.set_access_log, None)
+        self.assertRaises(AttributeError, vhost.set_warning_log, None)
+
+    def test_ip(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))
+        self.assertEqual(set(), vhost.get_ip())
+        vhost.add_ip('10.0.0.0/8')
+        vhost.add_ip('192.168.0.0/16')
+        self.assertEqual(set(['10.0.0.0/8', '192.168.0.0/16']), vhost.get_ip())
+        vhost.remove_ip('10.0.0.0/8')
+        self.assertEqual(set(['192.168.0.0/16']), vhost.get_ip())
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']))
+        self.assertEqual(set(['127.0.0.0/8', '192.168.0.0/16']), 
vhost.get_ip())
+
+    def test_host(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))h
+        self.assertEqual(set(), vhost.get_host())
+        vhost.add_host('foo.bar.com')
+        vhost.add_host('test.me.org')
+        self.assertEqual(set(['foo.bar.com', 'test.me.org']), vhost.get_host())
+        vhost.remove_host('foo.bar.com')
+        self.assertEqual(set(['test.me.org']), vhost.get_host())
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']),
+                      set(['test.me.org']))
+        self.assertEqual(set(['test.me.org']), vhost.get_host())
+
+    def test_host_use_regex(self):
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'))h
+        self.assertEqual(None, vhost.get_host_use_regex())
+        vhost.set_host_use_regex(True)
+        self.assertEqual(True, vhost.get_use_regex())
+        vhost.set_host_use_regex(None)
+        self.assertEqual(None, vhost.get_host_use_regex())
+        vhost = Vhost('test vhost', 80, 'HTTP', '/www', '/system',
+                      Log('ACCESSLOG'), Log('WARNINGLOG'),
+                      set(['127.0.0.0/8', '192.168.0.0/16']),
+                      set(['host.domain']), True)
+        self.assertEqual(True, vhost.get_use_regex())



commit 101f031c7d413535def126b9f59a8f918e8d8468
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 1 18:11:12 2009 +0200

    Implemented Log class.

diff --git a/misc/py_control_client/pycontrollib/log.py 
b/misc/py_control_client/pycontrollib/log.py
index b813ccc..44a65a0 100644
--- a/misc/py_control_client/pycontrollib/log.py
+++ b/misc/py_control_client/pycontrollib/log.py
@@ -20,6 +20,8 @@ from lxml import etree
 
 class Stream():
     def __init__(self, location, cycle = None, cycle_gzip = None, filter = []):
+        if location is None:
+            raise AttributeError('location is required and can\'t be None')
         self.location = location
         self.cycle = cycle
         self.cycle_gzip = cycle_gzip
@@ -113,3 +115,82 @@ class Stream():
             element.text = filter
             root.append(element)
         return root
+
+class Log():
+    def __init__(self, log_type, stream = [], type = None):
+        if log_type is None:
+            raise AttributeError('log_type is required and can\'t be None')
+        self.log_type = log_type
+        self.stream = stream
+        self.type = type
+
+    def __eq__(self, other):
+        return isinstance(other, Log) and \
+            self.log_type == other.log_type and \
+            self.stream == other.stream and \
+            self.type == other.type
+    
+    def get_type(self):
+        '''Get log's type attribute.'''
+        return self.type
+
+    def set_type(self, type):
+        '''Set log's type attribute, None means not set'''
+        self.type = type
+
+    def get_log_type(self):
+        '''Get log's type.'''
+        return self.log_type
+
+    def set_log_type(self, log_type):
+        '''Set log's type.'''
+        if log_type is None:
+            raise AttributeError('log_type is required and can\'t be None')
+        self.log_type = log_type
+
+    def get_streams(self):
+        '''Get streams associated with this log.'''
+        return self.stream
+
+    def add_stream(self, stream, index = None):
+        '''Append stream to current streams or if index is not None insert
+        stream ad index-th position.'''
+        if index is None:
+            self.stream.append(stream)
+        else:
+            self.stream.insert(index, stream)
+
+    def remove_stream(self, index):
+        '''Remove stream from index-th position.'''
+        self.stream.pop(index)
+    
+    def get_stream(self, index):
+        '''Get index-th stream.'''
+        return self.stream[index]
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce log by parsing a string.'''
+        return Log.from_lxml_element(etree.XML(text))
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce log from etree.Element object.'''
+        log_type = root.tag
+        type = root.get('type', None)
+        stream = []
+        for child in list(root):
+            if child.tag == 'STREAM':
+                stream.append(Stream.from_lxml_element(child))
+        return Log(log_type, stream, type)
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    def to_lxml_element(self):
+        root = etree.Element(self.log_type)
+        if self.type is not None:
+            root.set('type', self.type)
+        for stream in self.stream:
+            root.append(stream.to_lxml_element())
+        return root
diff --git a/misc/py_control_client/pycontrollib/log_test.py 
b/misc/py_control_client/pycontrollib/log_test.py
index 1bf968d..55c86a0 100644
--- a/misc/py_control_client/pycontrollib/log_test.py
+++ b/misc/py_control_client/pycontrollib/log_test.py
@@ -33,6 +33,7 @@ class StreamTest(unittest.TestCase):
         log.set_location('/logs/new.log')
         self.assertEqual('/logs/new.log', log.get_location())
         self.assertRaises(AttributeError, log.set_location, None)
+        self.assertRaises(AttributeError, Stream, None)
 
     def test_cycle(self):
         log = Stream('path')
@@ -154,8 +155,8 @@ class LogTest(unittest.TestCase):
     def test_creation(self):
         log = Log('ACCESSLOG')
         log = Log('WARNINGLOG')
-        log = Log('ACCESSLOG', [self.stream])
-        log = Log('ACCESSLOG', [self.stream], 'combined')
+        log = Log('ACCESSLOG', [self.stream_0])
+        log = Log('ACCESSLOG', [self.stream_0], 'combined')
 
     def test_log_type(self):
         log = Log('ACCESSLOG')
@@ -163,6 +164,7 @@ class LogTest(unittest.TestCase):
         log.set_log_type('WARNINGLOG')
         self.assertEqual('WARNINGLOG', log.get_log_type())
         self.assertRaises(AttributeError, log.set_log_type, None)
+        self.assertRaises(AttributeError, Log, None)
 
     def test_type(self):
         log = Log('ACCESSLOG')
@@ -210,14 +212,14 @@ class LogTest(unittest.TestCase):
         text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
             self.stream_0, self.stream_1)
         log = Log.from_string(text)
-        copy = Stream.from_string(str(log))
+        copy = Log.from_string(str(log))
         self.assertEqual(log, copy)
 
     def test_to_lxml(self):
         text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
             self.stream_0, self.stream_1)
         log = Log.from_string(text)
-        copy = Stream.from_lxml_element(log.to_lxml_element())
+        copy = Log.from_lxml_element(log.to_lxml_element())
         self.assertEqual(log, copy)
 
     def test_equality(self):



commit 8cb4b723a21d59214f73b9b5b82efbb7efae711d
Author: Marek Aaron Sapota <address@hidden>
Date:   Wed Jul 1 15:02:03 2009 +0200

    Tests for Log class.

diff --git a/misc/py_control_client/pycontrollib/log_test.py 
b/misc/py_control_client/pycontrollib/log_test.py
index 6480fa8..1bf968d 100644
--- a/misc/py_control_client/pycontrollib/log_test.py
+++ b/misc/py_control_client/pycontrollib/log_test.py
@@ -17,7 +17,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 import unittest
-from log import Stream
+from log import Stream, Log
 from lxml import etree
 
 class StreamTest(unittest.TestCase):
@@ -144,5 +144,92 @@ class StreamTest(unittest.TestCase):
         copy = Stream.from_lxml_element(log.to_lxml_element())
         self.assertEqual(log, copy)
 
+class LogTest(unittest.TestCase):
+    def setUp(self):
+        text_0 = '<STREAM location="file://logs/MyServerHTTP.log" />'
+        self.stream_0 = Stream.from_string(text_0)
+        text_1 = '<STREAM location="console://stdout" />'
+        self.stream_1 = Stream.from_string(text_1)
+
+    def test_creation(self):
+        log = Log('ACCESSLOG')
+        log = Log('WARNINGLOG')
+        log = Log('ACCESSLOG', [self.stream])
+        log = Log('ACCESSLOG', [self.stream], 'combined')
+
+    def test_log_type(self):
+        log = Log('ACCESSLOG')
+        self.assertEqual('ACCESSLOG', log.get_log_type())
+        log.set_log_type('WARNINGLOG')
+        self.assertEqual('WARNINGLOG', log.get_log_type())
+        self.assertRaises(AttributeError, log.set_log_type, None)
+
+    def test_type(self):
+        log = Log('ACCESSLOG')
+        self.assertEqual(None, log.get_type())
+        log.set_type('combined')
+        self.assertEqual('combined', log.get_type())
+        log.set_type(None)
+        self.assertEqual(None, log.get_type())
+        log = Log('ACCESSLOG', [], 'combined')
+        self.assertEqual('combined', log.get_type())
+
+    def test_stream(self):
+        log = Log('ACCESSLOG')
+        self.assertEqual([], log.get_streams())
+        log.add_stream(self.stream_0)
+        self.assertEqual([self.stream_0], log.get_streams())
+        log.add_stream(self.stream_1)
+        self.assertEqual([self.stream_0, self.stream_1], log.get_streams())
+        log.add_stream(self.stream_1, 0)
+        self.assertEqual([self.stream_1, self.stream_0, self.stream_1],
+                         log.get_streams())
+        self.assertEqual(self.stream_0, log.get_stream(1))
+        log.remove_stream(1)
+        self.assertEqual([self.stream_1, self.stream_1], log.get_streams())
+        log = Log('ACCESSLOG', [self.stream_0])
+        self.assertEqual([self.stream_0], log.get_streams())
+
+    def test_from_string(self):
+        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
+            self.stream_0, self.stream_1)
+        log = Log.from_string(text)
+        self.assertEqual('ACCESSLOG', log.get_log_type())
+        self.assertEqual('combined', log.get_type())
+        self.assertEqual([self.stream_0, self.stream_1], log.get_streams())
+
+    def test_from_lxml(self):
+        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
+            self.stream_0, self.stream_1)
+        log = Log.from_lxml_element(etree.XML(text))
+        self.assertEqual('ACCESSLOG', log.get_log_type())
+        self.assertEqual('combined', log.get_type())
+        self.assertEqual([self.stream_0, self.stream_1], log.get_streams())
+
+    def test_to_string(self):
+        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
+            self.stream_0, self.stream_1)
+        log = Log.from_string(text)
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_lxml(self):
+        text = '''<ACCESSLOG type="combined">{0}{1}</ACCESSLOG>'''.format( \
+            self.stream_0, self.stream_1)
+        log = Log.from_string(text)
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)
+
+    def test_equality(self):
+        self.assertEqual(Log('ACCESSLOG', [self.stream_0, self.stream_1], 
'combined'),
+                         Log('ACCESSLOG', [self.stream_0, self.stream_1], 
'combined'))
+        self.assertNotEqual(Log('ACCESSLOG', [self.stream_0], 'combined'),
+                            Log('WARNINGLOG', [self.stream_0], 'combined'))
+        self.assertNotEqual(Log('ACCESSLOG', [self.stream_0], 'combined'),
+                            Log('ACCESSLOG', [self.stream_1], 'combined'))
+        self.assertNotEqual(Log('ACCESSLOG', [self.stream_0], 'combined'),
+                            Log('WARNINGLOG', [self.stream_0], None))
+        self.assertNotEqual([], Log('ACCESSLOG'))
+
 if __name__ == '__main__':
     unittest.main()



commit 2e4765307dd1a8ac4f63d89af67a9544bc45a8dc
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jun 30 17:55:00 2009 +0200

    Log Stream class added.

diff --git a/misc/py_control_client/pycontrollib/log.py 
b/misc/py_control_client/pycontrollib/log.py
new file mode 100644
index 0000000..b813ccc
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/log.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+class Stream():
+    def __init__(self, location, cycle = None, cycle_gzip = None, filter = []):
+        self.location = location
+        self.cycle = cycle
+        self.cycle_gzip = cycle_gzip
+        self.filter = filter
+
+    def __eq__(self, other):
+        return isinstance(other, Stream) and \
+            self.location == other.location and \
+            self.cycle == other.cycle and \
+            self.cycle_gzip == other.cycle_gzip and \
+            self.filter == other.filter
+
+    def get_location(self):
+        '''Get stream location.'''
+        return self.location
+
+    def set_location(self, location):
+        '''Set stream location.'''
+        if location is None:
+            raise AttributeError('location is required and can\'t be None')
+        self.location = location
+
+    def get_cycle(self):
+        '''Get stream cycle value.'''
+        return self.cycle
+
+    def set_cycle(self, cycle):
+        '''Set cycle value, None means not set.'''
+        self.cycle = cycle
+
+    def get_cycle_gzip(self):
+        '''Get stream cycle_gzip value.'''
+        return self.cycle_gzip
+
+    def set_cycle_gzip(self, cycle_gzip):
+        '''Set stream cycle_gzip value, None means not set'''
+        self.cycle_gzip = cycle_gzip
+
+    def get_filters(self):
+        '''Get list of stream filters.'''
+        return self.filter
+
+    def get_filter(self, index):
+        '''Get index-th filter.'''
+        return self.filter[index]
+
+    def add_filter(self, filter, index = None):
+        '''Append a new filter, or insert it at index position.'''
+        if index is None:
+            self.filter.append(filter)
+        else:
+            self.filter.insert(index, filter)
+
+    def remove_filter(self, index):
+        '''Remove index-th filter.'''
+        self.filter.pop(index)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce stream by parsing a string.'''
+        return Stream.from_lxml_element(etree.XML(text))
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce stream from etree.Element object.'''
+        if root.tag != 'STREAM':
+            raise AttributeError('Expected STREAM tag.')
+        location = root.get('location')
+        cycle = root.get('cycle', None)
+        cycle_gzip = root.get('cycle_gzip', None)
+        if cycle_gzip is not None:
+            cycle_gzip = True if cycle_gzip == 'YES' else False
+        filter = []
+        for child in list(root):
+            if child.tag == 'FILTER':
+                filter.append(child.text)
+        return Stream(location, cycle, cycle_gzip, filter)
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    def to_lxml_element(self):
+        root = etree.Element('STREAM')
+        root.set('location', self.location)
+        if self.cycle is not None:
+            root.set('cycle', self.cycle)
+        if self.cycle_gzip is not None:
+            root.set('cycle_gzip', 'YES' if self.cycle_gzip else 'NO')
+        for filter in self.filter:
+            element = etree.Element('FILTER')
+            element.text = filter
+            root.append(element)
+        return root
diff --git a/misc/py_control_client/pycontrollib/log_test.py 
b/misc/py_control_client/pycontrollib/log_test.py
index 8d61ead..6480fa8 100644
--- a/misc/py_control_client/pycontrollib/log_test.py
+++ b/misc/py_control_client/pycontrollib/log_test.py
@@ -74,16 +74,16 @@ class StreamTest(unittest.TestCase):
         self.assertEqual(['gzip'], log.get_filters())
 
     def test_from_string(self):
-        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
         log = Stream.from_string(text)
         self.assertEqual('path', log.get_location())
-        self.assertEqual(123, log.get_cycle())
+        self.assertEqual('123', log.get_cycle())
         self.assertFalse(log.get_cycle_gzip())
         self.assertEqual(['gzip', 'bzip2'], log.get_filters())
-        text = '<STREAM location="path />'
+        text = '<STREAM location="path" />'
         log = Stream.from_string(text)
         self.assertEqual('path', log.get_location())
         self.assertEqual(None, log.get_cycle())
@@ -91,16 +91,16 @@ class StreamTest(unittest.TestCase):
         self.assertEqual([], log.get_filters())
 
     def test_from_lxml(self):
-        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
         log = Stream.from_lxml_element(etree.XML(text))
         self.assertEqual('path', log.get_location())
-        self.assertEqual(123, log.get_cycle())
+        self.assertEqual('123', log.get_cycle())
         self.assertFalse(log.get_cycle_gzip())
         self.assertEqual(['gzip', 'bzip2'], log.get_filters())
-        text = '<STREAM location="path />'
+        text = '<STREAM location="path" />'
         log = Stream.from_lxml_element(etree.XML(text))
         self.assertEqual('path', log.get_location())
         self.assertEqual(None, log.get_cycle())
@@ -116,17 +116,18 @@ class StreamTest(unittest.TestCase):
     def test_equality(self):
         self.assertEqual(Stream('path', 123, False, ['gzip', 'bzip2']),
                          Stream('path', 123, False, ['gzip', 'bzip2']))
-        self.assertEqual(Stream('path1', 123, False, ['gzip', 'bzip2']),
-                         Stream('path2', 123, False, ['gzip', 'bzip2']))
-        self.assertEqual(Stream('path', 1234, False, ['gzip', 'bzip2']),
-                         Stream('path', 123, False, ['gzip', 'bzip2']))
-        self.assertEqual(Stream('path', 123, True, ['gzip', 'bzip2']),
-                         Stream('path', 123, False, ['gzip', 'bzip2']))
-        self.assertEqual(Stream('path', 123, True, ['bzip2']),
-                         Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertNotEqual(Stream('path1', 123, False, ['gzip', 'bzip2']),
+                            Stream('path2', 123, False, ['gzip', 'bzip2']))
+        self.assertNotEqual(Stream('path', 1234, False, ['gzip', 'bzip2']),
+                            Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertNotEqual(Stream('path', 123, True, ['gzip', 'bzip2']),
+                            Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertNotEqual(Stream('path', 123, True, ['bzip2']),
+                            Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertNotEqual([], Stream('path'))
 
     def test_to_string(self):
-        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
@@ -135,10 +136,13 @@ class StreamTest(unittest.TestCase):
         self.assertEqual(log, copy)
 
     def test_to_lxml(self):
-        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+        text = '''<STREAM location="path" cycle="123" cycle_gzip="NO">
   <FILTER>gzip</FILTER>
   <FILTER>bzip2</FILTER>
 </STREAM>'''
         log = Stream.from_string(text)
         copy = Stream.from_lxml_element(log.to_lxml_element())
         self.assertEqual(log, copy)
+
+if __name__ == '__main__':
+    unittest.main()



commit fbdd0f4735ccddda0530adc0ab706ff1d8e8b12e
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jun 29 17:01:47 2009 +0200

    Tests for log stream.

diff --git a/misc/py_control_client/pycontrollib/log_test.py 
b/misc/py_control_client/pycontrollib/log_test.py
new file mode 100644
index 0000000..8d61ead
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/log_test.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 unittest
+from log import Stream
+from lxml import etree
+
+class StreamTest(unittest.TestCase):
+    def test_creation(self):
+        log = Stream('/logs/MyServer.log')
+        log = Stream('/logs/MyServer.log', 1048576)
+        log = Stream('/logs/MyServer.log', 1048576, False)
+        log = Stream('/logs/MyServer.log', 1048576, False, ['gzip', 'bzip2'])
+
+    def test_location(self):
+        log = Stream('/logs/MyServer.log')
+        self.assertEqual('/logs/MyServer.log', log.get_location())
+        log.set_location('/logs/new.log')
+        self.assertEqual('/logs/new.log', log.get_location())
+        self.assertRaises(AttributeError, log.set_location, None)
+
+    def test_cycle(self):
+        log = Stream('path')
+        self.assertEqual(None, log.get_cycle())
+        log.set_cycle(123)
+        self.assertEqual(123, log.get_cycle())
+        log.set_cycle(None)
+        self.assertEqual(None, log.get_cycle())
+        log = Stream('path', 123)
+        self.assertEqual(123, log.get_cycle())
+
+    def test_cycle_gzip(self):
+        log = Stream('path')
+        self.assertEqual(None, log.get_cycle_gzip())
+        log.set_cycle_gzip(True)
+        self.assertEqual(True, log.get_cycle_gzip())
+        log.set_cycle_gzip(None)
+        self.assertEqual(None, log.get_cycle_gzip())
+        log = Stream('path', 123, False)
+        self.assertEqual(False, log.get_cycle_gzip())
+        log = Stream('path', cycle_gzip = False)
+        self.assertEqual(False, log.get_cycle_gzip())
+
+    def test_filter(self):
+        log = Stream('path')
+        self.assertEqual([], log.get_filters())
+        log.add_filter('gzip')
+        self.assertEqual(['gzip'], log.get_filters())
+        log.add_filter('bzip2')
+        self.assertEqual(['gzip', 'bzip2'], log.get_filters())
+        log.add_filter('bzip2', 0)
+        self.assertEqual(['bzip2', 'gzip', 'bzip2'], log.get_filters())
+        self.assertEqual('gzip', log.get_filter(1))
+        log.remove_filter(1)
+        self.assertEqual(['bzip2', 'bzip2'], log.get_filters())
+        log = Stream('path', 123, False, ['gzip'])
+        self.assertEqual(['gzip'], log.get_filters())
+        log = Stream('path', filter = ['gzip'])
+        self.assertEqual(['gzip'], log.get_filters())
+
+    def test_from_string(self):
+        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+  <FILTER>gzip</FILTER>
+  <FILTER>bzip2</FILTER>
+</STREAM>'''
+        log = Stream.from_string(text)
+        self.assertEqual('path', log.get_location())
+        self.assertEqual(123, log.get_cycle())
+        self.assertFalse(log.get_cycle_gzip())
+        self.assertEqual(['gzip', 'bzip2'], log.get_filters())
+        text = '<STREAM location="path />'
+        log = Stream.from_string(text)
+        self.assertEqual('path', log.get_location())
+        self.assertEqual(None, log.get_cycle())
+        self.assertEqual(None, log.get_cycle_gzip())
+        self.assertEqual([], log.get_filters())
+
+    def test_from_lxml(self):
+        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+  <FILTER>gzip</FILTER>
+  <FILTER>bzip2</FILTER>
+</STREAM>'''
+        log = Stream.from_lxml_element(etree.XML(text))
+        self.assertEqual('path', log.get_location())
+        self.assertEqual(123, log.get_cycle())
+        self.assertFalse(log.get_cycle_gzip())
+        self.assertEqual(['gzip', 'bzip2'], log.get_filters())
+        text = '<STREAM location="path />'
+        log = Stream.from_lxml_element(etree.XML(text))
+        self.assertEqual('path', log.get_location())
+        self.assertEqual(None, log.get_cycle())
+        self.assertEqual(None, log.get_cycle_gzip())
+        self.assertEqual([], log.get_filters())
+
+    def test_bad_root_tag(self):
+        text = '<ERROR location="path" />'
+        self.assertRaises(AttributeError, Stream.from_string, text)
+        self.assertRaises(AttributeError, Stream.from_lxml_element,
+                          etree.XML(text))
+    
+    def test_equality(self):
+        self.assertEqual(Stream('path', 123, False, ['gzip', 'bzip2']),
+                         Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertEqual(Stream('path1', 123, False, ['gzip', 'bzip2']),
+                         Stream('path2', 123, False, ['gzip', 'bzip2']))
+        self.assertEqual(Stream('path', 1234, False, ['gzip', 'bzip2']),
+                         Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertEqual(Stream('path', 123, True, ['gzip', 'bzip2']),
+                         Stream('path', 123, False, ['gzip', 'bzip2']))
+        self.assertEqual(Stream('path', 123, True, ['bzip2']),
+                         Stream('path', 123, False, ['gzip', 'bzip2']))
+
+    def test_to_string(self):
+        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+  <FILTER>gzip</FILTER>
+  <FILTER>bzip2</FILTER>
+</STREAM>'''
+        log = Stream.from_string(text)
+        copy = Stream.from_string(str(log))
+        self.assertEqual(log, copy)
+
+    def test_to_lxml(self):
+        text = '''<STREAM location="path" cycle=123 cycle_gzip="NO">
+  <FILTER>gzip</FILTER>
+  <FILTER>bzip2</FILTER>
+</STREAM>'''
+        log = Stream.from_string(text)
+        copy = Stream.from_lxml_element(log.to_lxml_element())
+        self.assertEqual(log, copy)



commit 5064583a62c20962fd089ff747b8d0d9538a574b
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jun 28 21:41:05 2009 +0200

    Extended mime types classes and tests for them.

diff --git a/misc/py_control_client/pycontrollib/mimetypes.py 
b/misc/py_control_client/pycontrollib/mimetypes.py
index b9f4877..a6a4190 100644
--- a/misc/py_control_client/pycontrollib/mimetypes.py
+++ b/misc/py_control_client/pycontrollib/mimetypes.py
@@ -16,23 +16,19 @@ You should have received a copy of the GNU General Public 
License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
+from definition import Definition
 from lxml import etree
 
 class MIMEType():
     valid_handlers = set(['SEND', 'CGI', 'FASTCGI', 'SCGI', 'MSCGI', 'ISAPI',
                           'WINCGI', 'PROxY'])
 
-    class WrongArguments(Exception):
-        '''Exception raised when MIMEType is constructed with invalid
-        arguments.'''
-        pass
-
-    def __init__(self, mime, handler, param, extension, path = None,
-                 filter = None, self_executed = None, definitions = None):
+    def __init__(self, mime, handler, param = None, extension = set(), path = 
None,
+                 filter = [], self_executed = None, definitions = []):
         '''Creates new MIMEType with specified attributes.'''
         self.mime = mime
         if handler not in MIMEType.valid_handlers:
-            raise MIMEType.WrongArguments(
+            raise AttributeError(
                 '{0} is not a valid handler'.format(handler))
         self.handler = handler
         self.param = param
@@ -41,7 +37,101 @@ class MIMEType():
         self.filter = filter
         self.self_executed = self_executed
         self.definitions = definitions
-        
+
+    def get_mime(self):
+        '''Get associated mime type.'''
+        return self.mime
+
+    def set_mime(self, mime):
+        '''Set associated mime type.'''
+        self.mime = mime
+
+    def get_handler(self):
+        '''Get associated handler.'''
+        return self.handler
+
+    def set_handler(self, handler):
+        '''Set associated handler.'''
+        if handler not in MIMEType.valid_handlers:
+            raise AttributeError(
+                '{0} is not a valid handler'.format(handler))
+        self.handler = handler
+
+    def get_param(self):
+        '''Get associated param.'''
+        return self.param
+
+    def set_param(self, param):
+        '''Set associated param. None means no param.'''
+        self.param = param
+
+    def get_extensions(self):
+        '''Get associated extensions.'''
+        return self.extension
+
+    def remove_extension(self, extension):
+        '''Remove extension from associated extensions.'''
+        self.extension.remove(extension)
+
+    def add_extension(self, extension):
+        '''Add extension to associated extensions.'''
+        self.extension.add(extension)
+
+    def get_path(self):
+        '''Get associated path.'''
+        return self.path
+
+    def set_path(self, path):
+        '''Set associated path. None means no path.'''
+        self.path = path
+
+    def get_filters(self):
+        '''Get associated filters.'''
+        return self.filter
+
+    def get_filter(self, index):
+        '''Get filter with given index.'''
+        return self.filter[index]
+
+    def remove_filter(self, index):
+        '''Remove filter with given index.'''
+        self.filter.pop(index)
+
+    def add_filter(self, filter, index = None):
+        '''Append filter after all other filters, or insert it at index.'''
+        if index is None:
+            self.filter.append(filter)
+        else:
+            self.filter.insert(index, filter)
+
+    def get_self_executed(self):
+        '''Get self_executed setting.'''
+        return self.self_executed
+
+    def set_self_executed(self, self_executed):
+        '''Set self_executed setting.'''
+        self.self_executed = self_executed
+
+    def get_definitions(self):
+        '''Get all definitions.'''
+        return self.definitions
+    
+    def get_definition(self, index):
+        '''Get definition with given index.'''
+        return self.definitions[index]
+
+    def add_definition(self, definition, index = None):
+        '''Append definition after all other definitions, or insert it at
+        index.'''
+        if index is None:
+            self.definitions.append(definition)
+        else:
+            self.definitions.insert(index, definition)
+
+    def remove_definition(self, index):
+        '''Remove definition with given index.'''
+        self.definitions.pop(index)
+
     def __eq__(self, other):
         return \
             self.mime == other.mime and \
@@ -60,42 +150,54 @@ class MIMEType():
             return element
         def make_extension_element(extension):
             return make_element('EXTENSION', 'value', extension)
+        def make_filter_element(filter):
+            return make_element('FILTER', 'value', filter)
         root = etree.Element('MIME')
         root.set('mime', self.mime)
         root.set('handler', self.handler)
-        root.set('param', self.param)
+        if self.param is not None:
+            root.set('param', self.param)
         if self.self_executed is not None:
             root.set('self', 'YES' if self.self_executed else 'NO')
-        for element in map(make_extension_element, self.extension):
-            root.append(element)
         if self.path is not None:
             root.append(make_element('PATH', 'regex', self.path))
-        if self.filter is not None:
-            root.append(make_element('FILTER', 'value', self.filter))
+        for element in map(make_extension_element, self.extension):
+            root.append(element)
+        for element in map(make_filter_element, self.filter):
+            root.append(element)
+        for definition in self.definitions:
+            root.append(definition.to_lxml_element())
         return root
 
-    def __str__(self): # TODO: definitions
+    def __str__(self):
         return etree.tostring(self.to_lxml_element(), pretty_print = True)
 
     @staticmethod
-    def from_lxml_element(root): # TODO: definitions
+    def from_lxml_element(root):
         '''Factory to produce MIMEType from etree.Element object.'''
+        if root.tag != 'MIME':
+            raise AttributeError('Expected MIME tag.')
         mime = root.get('mime', None)
         handler = root.get('handler', None)
         param = root.get('param', None)
         self_executed = root.get('self', None)
         if self_executed is not None:
-            self_executed = False if self_executed == 'NO' else True
-        extension = set(map(lambda element: element.get('value'),
-                            root.findall('EXTENSION')))
-        path = root.find('PATH')
-        if path is not None:
-            path = path.get('regex')
-        filter = root.find('FILTER')
-        if filter is not None:
-            filter = filter.get('value')
+            self_executed = self_executed == 'YES'
+        path = None
+        extension = set()
+        filter = []
+        definitions = []
+        for child in list(root):
+            if child.tag == 'PATH':
+                path = child.get('regex')
+            elif child.tag == 'FILTER':
+                filter.append(child.get('value'))
+            elif child.tag == 'EXTENSION':
+                extension.add(child.get('value'))
+            elif child.tag == 'DEFINE':
+                definitions.append(Definition.from_lxml_element(child))
         return MIMEType(mime, handler, param, extension, path, filter,
-                        self_executed, None)
+                        self_executed, definitions)
 
     @staticmethod
     def from_string(text):
@@ -121,7 +223,9 @@ class MIMETypes():
     @staticmethod
     def from_lxml_element(root):
         '''Factory to produce MIMETypes from etree.Element object.'''
-        return MIMETypes(map(MIMEType.from_lxml_element, root.findall('MIME')))
+        if root.tag != 'MIMES':
+            raise AttributeError('Expected MIMES tag.')
+        return MIMETypes(map(MIMEType.from_lxml_element, list(root)))
     
     @staticmethod
     def from_string(text):
diff --git a/misc/py_control_client/pycontrollib/mimetypes_test.py 
b/misc/py_control_client/pycontrollib/mimetypes_test.py
index ec96779..607e2cb 100644
--- a/misc/py_control_client/pycontrollib/mimetypes_test.py
+++ b/misc/py_control_client/pycontrollib/mimetypes_test.py
@@ -17,38 +17,129 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
 '''
 
 import unittest
+from lxml import etree
 from mimetypes import MIMEType, MIMETypes
+from definition import Definition
 
 class MIMETypeTest(unittest.TestCase):
-    text = '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
+    def setUp(self):
+        self.definitions = []
+        self.definitions.append(Definition.from_string(
+                '<DEFINE name="http.use_error_file" value="YES" />'))
+        self.definitions.append(Definition.from_string(
+                '<DEFINE name="http.error.file.404" value="404.html" />'))
+        self.definitions.append(Definition.from_string(
+                '''<DEFINE name="http.default_file">
+  <DEFINE value="index.html" />
+  <DEFINE value="index.htm" />
+</DEFINE>'''))
+        self.text = \
+            '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
   <EXTENSION value="xhtml" />
   <EXTENSION value="xml" />
   <EXTENSION value="py" />
   <FILTER value="gzip" />
+  <FILTER value="bzip2" />
+  {0}
   <PATH regex="^/cgi-bin/python/.*$" />
-</MIME>'''
+</MIME>'''.format('\n'.join(map(lambda element: str(element),
+                                self.definitions)))
     
     def test_from_string(self):
-        mime = MIMEType.from_string(MIMETypeTest.text)
-        self.assertEqual(mime.mime, 'application/xhtml+xml')
-        self.assertEqual(mime.handler, 'CGI')
-        self.assertEqual(mime.param, '/usr/bin/python')
-        self.assertEqual(mime.extension, set(['xhtml', 'xml', 'py']))
-        self.assertEqual(mime.path, '^/cgi-bin/python/.*$')
-        self.assertEqual(mime.filter, 'gzip')
-        self.assertEqual(mime.self_executed, False)
+        mime = MIMEType.from_string(self.text)
+        self.assertEqual(mime.get_mime(), 'application/xhtml+xml')
+        self.assertEqual(mime.get_handler(), 'CGI')
+        self.assertEqual(mime.get_param(), '/usr/bin/python')
+        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'py']))
+        self.assertEqual(mime.get_path(), '^/cgi-bin/python/.*$')
+        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2'])
+        self.assertEqual(mime.get_self_executed(), False)
+        self.assertEqual(mime.get_definitions(), self.definitions)
+
+    def test_from_lxml(self):
+        mime = MIMEType.from_lxml_element(etree.XML(self.text))
+        self.assertEqual(mime.get_mime(), 'application/xhtml+xml')
+        self.assertEqual(mime.get_handler(), 'CGI')
+        self.assertEqual(mime.get_param(), '/usr/bin/python')
+        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'py']))
+        self.assertEqual(mime.get_path(), '^/cgi-bin/python/.*$')
+        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2'])
+        self.assertEqual(mime.get_self_executed(), False)
+        self.assertEqual(mime.get_definitions(), self.definitions)
+
+    def test_handler(self):
+        mime = MIMEType.from_string(self.text)
+        for handler in MIMEType.valid_handlers:
+            mime.set_handler(handler)
+        self.assertRaises(AttributeError, mime.set_handler, 'ERROR')
+        
+    def test_attributes(self):
+        mime = MIMEType.from_string(self.text)
+        mime.set_mime('plain/text')
+        self.assertEqual(mime.get_mime(), 'plain/text')
+        mime.set_handler('SEND')
+        self.assertEqual(mime.get_handler(), 'SEND')
+        mime.set_param('')
+        self.assertEqual(mime.get_param(), '')
+        mime.remove_extension('py')
+        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml']))
+        mime.add_extension('pyc')
+        self.assertEqual(mime.get_extensions(), set(['xhtml', 'xml', 'pyc']))
+        mime.set_path('^/python/')
+        self.assertEqual(mime.get_path(), '^/python/')
+        mime.remove_filter(0)
+        self.assertEqual(mime.get_filters(), ['bzip2'])
+        mime.add_filter('gzip')
+        self.assertEqual(mime.get_filters(), ['bzip2', 'gzip'])
+        mime.add_filter('gzip', 0)
+        self.assertEqual(mime.get_filters(), ['gzip', 'bzip2', 'gzip'])
+        self.assertEqual(mime.get_filter(1), 'bzip2')
+        mime.set_self_executed(True)
+        self.assertEqual(mime.get_self_executed(), True)
+        definition = Definition.from_string(
+            '<DEFINE name="http.error.file.404" value="404.html" />')
+        mime.add_definition(definition)
+        self.definitions.append(definition)
+        self.assertEqual(mime.get_definitions(), self.definitions)
+        mime.add_definition(definition, 0)
+        self.definitions.insert(0, definition)
+        self.assertEqual(mime.get_definitions(), self.definitions)
+        mime.remove_definition(1)
+        self.definitions.pop(1)
+        self.assertEqual(mime.get_definitions(), self.definitions)
+        self.assertEqual(mime.get_definition(1), self.definitions[1])
+        
+    def test_equality(self):
+        self.assertEqual(MIMEType.from_string(self.text),
+                         MIMEType.from_string(self.text))
+        self.assertNotEqual(MIMEType.from_string(self.text),
+                            MIMEType('text/plain', 'SEND'))
         
     def test_to_string(self):
-        mime = MIMEType.from_string(MIMETypeTest.text)
+        mime = MIMEType.from_string(self.text)
         copy = MIMEType.from_string(str(mime))
         self.assertEqual(mime, copy)
         
+    def test_to_lxml(self):
+        mime = MIMEType.from_string(self.text)
+        copy = MIMEType.from_lxml_element(mime.to_lxml_element())
+        self.assertEqual(mime, copy)
+        
+
+    def test_bad_root_tag(self):
+        text = '''<ERROR mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
+  <EXTENSION value="xhtml" />
+</ERROR>'''
+        self.assertRaises(AttributeError, MIMEType.from_string, text)
+        self.assertRaises(AttributeError, MIMEType.from_lxml_element,
+                          etree.XML(text))
+        
 class MIMETypesTest(unittest.TestCase):
-    mime_0 = '''<MIME mime="text/html" handler="FASTCGI" self="YES" param="">
+    def setUp(self):
+        self.mime_0 = '''<MIME mime="text/html" handler="FASTCGI" self="YES" 
param="">
   <EXTENSION value="fcgi"/>
 </MIME>'''
-
-    mime_1 = '''<MIME mime="text/plain" handler="SEND" param="">
+        self.mime_1 = '''<MIME mime="text/plain" handler="SEND" param="">
   <EXTENSION value="asc"/>
   <EXTENSION value="c"/>
   <EXTENSION value="cc"/>
@@ -59,20 +150,41 @@ class MIMETypesTest(unittest.TestCase):
   <EXTENSION value="m"/>
   <EXTENSION value="txt"/>
 </MIME>'''
-    
-    text = '<MIMES>{0}{1}</MIMES>'.format(mime_0, mime_1)
+        self.text = '<MIMES>{0}{1}</MIMES>'.format(self.mime_0, self.mime_1)
     
     def test_from_string(self):
-        mimes = MIMETypes.from_string(MIMETypesTest.text)
-        self.assertEqual(mimes.MIME_types[0],
-                         MIMEType.from_string(MIMETypesTest.mime_0))
-        self.assertEqual(mimes.MIME_types[1],
-                         MIMEType.from_string(MIMETypesTest.mime_1))
+        mimes = MIMETypes.from_string(self.text)
+        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
+        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
+
+    def test_from_lxml(self):
+        mimes = MIMETypes.from_lxml_element(etree.XML(self.text))
+        self.assertEqual(mimes.MIME_types[0], 
MIMEType.from_string(self.mime_0))
+        self.assertEqual(mimes.MIME_types[1], 
MIMEType.from_string(self.mime_1))
         
     def test_to_string(self):
-        mimes = MIMETypes.from_string(MIMETypesTest.text)
+        mimes = MIMETypes.from_string(self.text)
         copy = MIMETypes.from_string(str(mimes))
         self.assertEqual(mimes, copy)
         
+    def test_to_lxml(self):
+        mimes = MIMETypes.from_string(self.text)
+        copy = MIMETypes.from_lxml_element(mimes.to_lxml_element())
+        self.assertEqual(mimes, copy)
+
+    def test_equality(self):
+        self.assertEqual(MIMETypes.from_string(self.text),
+                         MIMETypes.from_string(self.text))
+        self.assertNotEqual(MIMETypes.from_string(self.text),
+                            MIMETypes.from_string(
+                '<MIMES>{0}</MIMES>'.format(self.mime_0)))
+
+    def test_bad_root_tag(self):
+        text = '<ERROR>{0}{1}</ERROR>'.format(
+            self.mime_0, self.mime_1)
+        self.assertRaises(AttributeError, MIMETypes.from_string, text)
+        self.assertRaises(AttributeError, MIMETypes.from_lxml_element,
+                          etree.XML(text))
+        
 if __name__ == '__main__':
     unittest.main()



commit c32449cf08ef4588c38a8fbd679db03c0c76be4c
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jun 28 21:36:28 2009 +0200

    Fix equality of definitions an other types.

diff --git a/misc/py_control_client/pycontrollib/definition.py 
b/misc/py_control_client/pycontrollib/definition.py
index aa3dc94..1344723 100644
--- a/misc/py_control_client/pycontrollib/definition.py
+++ b/misc/py_control_client/pycontrollib/definition.py
@@ -30,7 +30,9 @@ class Definition():
         self.attributes = attributes
 
     def __eq__(self, other):
-        return self.name == other.name and self.attributes == other.attributes
+        return isinstance(other, Definition) and \
+            self.name == other.name and \
+            self.attributes == other.attributes
 
     def get_name(self):
         '''Definition name, None means no name.'''
@@ -83,7 +85,8 @@ class DefinitionElement(Definition):
             Definition.__init__(self, name, attributes)
 
     def __eq__(self, other):
-        return Definition.__eq__(self, other)
+        return isinstance(other, DefinitionElement) and \
+            Definition.__eq__(self, other)
 
     def set_attribute(self, key, value):
         '''Set attribute key to given value.'''
@@ -128,7 +131,9 @@ class DefinitionTree(Definition):
         self.values = values
 
     def __eq__(self, other):
-        return Definition.__eq__(self, other) and self.values == other.values
+        return isinstance(other, DefinitionTree) and \
+            Definition.__eq__(self, other) and \
+            self.values == other.values
 
     def get_values(self):
         '''Get all sub-definitions.'''
diff --git a/misc/py_control_client/pycontrollib/definition_test.py 
b/misc/py_control_client/pycontrollib/definition_test.py
index 387ba57..adf3533 100644
--- a/misc/py_control_client/pycontrollib/definition_test.py
+++ b/misc/py_control_client/pycontrollib/definition_test.py
@@ -71,6 +71,9 @@ class DefinitionTest(unittest.TestCase):
         self.assertRaises(AttributeError, Definition.from_string, text)
         self.assertRaises(AttributeError, Definition.from_lxml_element,
                           etree.XML(text))
+
+    def test_equality(self):
+        self.assertNotEqual(Definition(), 'different type')
     
 class DefinitionElementTest(unittest.TestCase):
     def test_creation(self):
@@ -136,6 +139,7 @@ host="localhost" port="2010" local="yes" uid="1000" 
gid="1000" />'''
                             DefinitionElement('name2'))
         self.assertNotEqual(DefinitionElement('name', {'attribute': '1'}),
                             DefinitionElement('name', {'attribute': '2'}))
+        self.assertNotEqual(DefinitionElement(), 'different type')
 
     def test_to_string(self):
         text = '''<DEFINE server="/opt/bin/fastcgi_server" domain="fastcgi"
@@ -231,6 +235,7 @@ class DefinitionTreeTest(unittest.TestCase):
             self.element_1, self.element_2)
         self.assertEqual(DefinitionTree.from_string(text), 
DefinitionTree.from_string(text))
         self.assertNotEqual(DefinitionTree.from_string(text), 
DefinitionTree('test'))
+        self.assertNotEqual(DefinitionTree(), 'different type')
 
     def test_to_string(self):
         text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(



commit 4a5ec3753456e100c888234c97712fcd4f039237
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jun 28 20:28:44 2009 +0200

    Check if tag names are right when creating definitions.

diff --git a/misc/py_control_client/pycontrollib/definition.py 
b/misc/py_control_client/pycontrollib/definition.py
index 9074903..aa3dc94 100644
--- a/misc/py_control_client/pycontrollib/definition.py
+++ b/misc/py_control_client/pycontrollib/definition.py
@@ -58,6 +58,8 @@ class Definition():
     def from_lxml_element(root):
         '''Factory to produce definition element or tree from etree.Element
         object.'''
+        if root.tag != 'DEFINE':
+            raise AttributeError('Expected DEFINE tag.')
         if len(list(root)):
             return DefinitionTree.from_lxml_element(root)
         else:
@@ -105,6 +107,8 @@ class DefinitionElement(Definition):
     @staticmethod
     def from_lxml_element(root):
         '''Factory to produce definition element from etree.Element object.'''
+        if root.tag != 'DEFINE':
+            raise AttributeError('Expected DEFINE tag.')
         attributes = root.attrib
         name = attributes.pop('name', None)
         return DefinitionElement(name, attributes)
@@ -149,6 +153,8 @@ class DefinitionTree(Definition):
     @staticmethod
     def from_lxml_element(root):
         '''Factory to produce definition tree from etree.Element object.'''
+        if root.tag != 'DEFINE':
+            raise AttributeError('Expected DEFINE tag.')
         attributes = root.attrib
         name = attributes.pop('name', None)
         values = map(Definition.from_lxml_element, list(root))
diff --git a/misc/py_control_client/pycontrollib/definition_test.py 
b/misc/py_control_client/pycontrollib/definition_test.py
index 3c5917c..387ba57 100644
--- a/misc/py_control_client/pycontrollib/definition_test.py
+++ b/misc/py_control_client/pycontrollib/definition_test.py
@@ -66,6 +66,12 @@ class DefinitionTest(unittest.TestCase):
         definition = Definition.from_lxml_element(etree.XML(text))
         self.assertTrue(isinstance(definition, DefinitionTree))
 
+    def test_bad_root_tag(self):
+        text = '<ERROR name="http.error.file.404" value="404.html" />'
+        self.assertRaises(AttributeError, Definition.from_string, text)
+        self.assertRaises(AttributeError, Definition.from_lxml_element,
+                          etree.XML(text))
+    
 class DefinitionElementTest(unittest.TestCase):
     def test_creation(self):
         definition = DefinitionElement()
@@ -146,6 +152,12 @@ domain="fastcgi" host="localhost" port="2010" local="yes" 
uid="1000" gid="1000"
         copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
 
+    def test_bad_root_tag(self):
+        text = '<ERROR name="http.error.file.404" value="404.html" />'
+        self.assertRaises(AttributeError, Definition.from_string, text)
+        self.assertRaises(AttributeError, Definition.from_lxml_element,
+                          etree.XML(text))
+
 class DefinitionTreeTest(unittest.TestCase):
     def setUp(self):
         self.element_1 = DefinitionElement('http.error.file.404',
@@ -234,5 +246,12 @@ class DefinitionTreeTest(unittest.TestCase):
         copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)
 
+    def test_bad_root_tag(self):
+        text = '<ERROR name="test" key="value">{0}{1}</ERROR>'.format(
+            self.element_1, self.element_2)
+        self.assertRaises(AttributeError, Definition.from_string, text)
+        self.assertRaises(AttributeError, Definition.from_lxml_element,
+                          etree.XML(text))
+
 if __name__ == '__main__':
     unittest.main()



commit 58e336c9d612a1db3073b80d079652eae6dd1cbc
Author: Marek Aaron Sapota <address@hidden>
Date:   Sun Jun 28 19:01:02 2009 +0200

    Changed definitions, so only DefinitionElement accepts value attribute.

diff --git a/misc/py_control_client/pycontrollib/definition.py 
b/misc/py_control_client/pycontrollib/definition.py
index 1d7e6c4..9074903 100644
--- a/misc/py_control_client/pycontrollib/definition.py
+++ b/misc/py_control_client/pycontrollib/definition.py
@@ -24,6 +24,8 @@ class Definition():
 
     def __init__(self, name = None, attributes = {}):
         '''Creates new definition with given name and attributes.'''
+        if attributes.has_key('value'):
+            raise KeyError('value is not an allowed key')
         self.name = name
         self.attributes = attributes
 
@@ -44,6 +46,8 @@ class Definition():
 
     def set_attribute(self, key, value):
         '''Set attribute key to given value.'''
+        if key == 'value':
+            raise KeyError('value is not an allowd key')
         self.attributes[key] = value
 
     def remove_attribute(self, key):
@@ -69,11 +73,23 @@ class DefinitionElement(Definition):
 
     def __init__(self, name = None, attributes = {}):
         '''Creates new definition element with given name and attributes.'''
-        Definition.__init__(self, name, attributes)
+        if attributes.has_key('value'):
+            value = attributes.pop('value')
+            Definition.__init__(self, name, attributes)
+            self.attributes['value'] = value
+        else:
+            Definition.__init__(self, name, attributes)
 
     def __eq__(self, other):
         return Definition.__eq__(self, other)
 
+    def set_attribute(self, key, value):
+        '''Set attribute key to given value.'''
+        if key == 'value':
+            self.attributes['value'] = value
+        else:
+            Definition.set_attribute(self, key, value)
+
     def to_lxml_element(self):
         '''Convert definition element to etree.Element object.'''
         root = etree.Element('DEFINE')
diff --git a/misc/py_control_client/pycontrollib/definition_test.py 
b/misc/py_control_client/pycontrollib/definition_test.py
index d4b06f6..3c5917c 100644
--- a/misc/py_control_client/pycontrollib/definition_test.py
+++ b/misc/py_control_client/pycontrollib/definition_test.py
@@ -23,14 +23,8 @@ import unittest
 class DefinitionTest(unittest.TestCase):
     def test_creation(self):
         definition = Definition()
-        self.assertEqual(None, definition.get_name())
-        self.assertEqual({}, definition.attributes)
         definition = Definition('no attributes')
-        self.assertEqual('no attributes', definition.get_name())
-        self.assertEqual({}, definition.attributes)
         definition = Definition('with attributes', {'a': 'b', 'c': 'd'})
-        self.assertEqual('with attributes', definition.get_name())
-        self.assertEqual({'a': 'b', 'c': 'd'}, definition.attributes)
 
     def test_name(self):
         definition = Definition('name1')
@@ -51,6 +45,11 @@ class DefinitionTest(unittest.TestCase):
         self.assertEqual(None, definition.get_attribute('e'))
         self.assertRaises(KeyError, definition.remove_attribute, 'e')
 
+    def test_value(self):
+        self.assertRaises(KeyError, Definition, 'name', {'value': 'x'})
+        definition = Definition('name')
+        self.assertRaises(KeyError, definition.set_attribute, 'value', 'x')
+
     def test_from_string(self):
         text = '<DEFINE name="http.error.file.404" value="404.html" />'
         definition = Definition.from_string(text)
@@ -76,6 +75,14 @@ class DefinitionElementTest(unittest.TestCase):
         definition = DefinitionElement('name', {'value': 'value'})
         self.assertTrue(isinstance(definition, Definition))
 
+    def test_value(self):
+        definition = DefinitionElement('name', {'value': 'x'})
+        self.assertEqual('x', definition.get_attribute('value'))
+        definition.set_attribute('value', 'y')
+        self.assertEqual('y', definition.get_attribute('value'))
+        definition.remove_attribute('value')
+        self.assertEqual(None, definition.get_attribute('value'))
+
     def test_from_string(self):
         text = '<DEFINE name="http.error.file.404" value="404.html" />'
         definition = DefinitionElement.from_string(text)
@@ -140,80 +147,89 @@ domain="fastcgi" host="localhost" port="2010" local="yes" 
uid="1000" gid="1000"
         self.assertEqual(definition, copy)
 
 class DefinitionTreeTest(unittest.TestCase):
-    element_1 = DefinitionElement('http.error.file.404', {'value': '404.html'})
-    element_2 = DefinitionElement(attributes = {'server': 
'/opt/bin/fastcgi_server',
-                                                'domain': 'fastcgi', 'host': 
'localhost',
-                                                'port': '2010', 'local': 'yes',
-                                                'uid': '1000', 'gid': '1000'})
+    def setUp(self):
+        self.element_1 = DefinitionElement('http.error.file.404',
+                                           {'value': '404.html'})
+        self.element_2 = DefinitionElement(
+            attributes = {'server': '/opt/bin/fastcgi_server',
+                          'domain': 'fastcgi', 'host': 'localhost',
+                          'port': '2010', 'local': 'yes',
+                          'uid': '1000', 'gid': '1000'})
+
     def test_creation(self):
         definition = DefinitionTree()
         self.assertTrue(isinstance(definition, Definition))
         definition = DefinitionTree('name')
         self.assertTrue(isinstance(definition, Definition))
-        definition = DefinitionTree('name', [DefinitionTreeTest.element_1])
+        definition = DefinitionTree('name', [self.element_1])
         self.assertTrue(isinstance(definition, Definition))
-        definition = DefinitionTree('name', [DefinitionTreeTest.element_2], 
{'value': '1'})
+        definition = DefinitionTree('name', [self.element_2], {'key': 'value'})
         self.assertTrue(isinstance(definition, Definition))
 
+    def test_value(self):
+        self.assertRaises(KeyError, DefinitionTree, 'name', [], {'value': 'x'})
+        definition = Definition('name')
+        self.assertRaises(KeyError, definition.set_attribute, 'value', 'x')
+
     def test_values(self):
         definition = DefinitionTree()
         self.assertEqual(0, len(definition.get_values()))
-        definition.add_value(DefinitionTreeTest.element_1)
+        definition.add_value(self.element_1)
         self.assertEqual(1, len(definition.get_values()))
-        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
-        definition.add_value(DefinitionTreeTest.element_2)
+        self.assertEqual(self.element_1, definition.get_value(0))
+        definition.add_value(self.element_2)
         self.assertEqual(2, len(definition.get_values()))
-        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
-        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(1))
+        self.assertEqual(self.element_1, definition.get_value(0))
+        self.assertEqual(self.element_2, definition.get_value(1))
         definition.remove_value(0)
         self.assertEqual(1, len(definition.get_values()))
-        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(0))
-        definition.add_value(DefinitionTreeTest.element_1, 0)
+        self.assertEqual(self.element_2, definition.get_value(0))
+        definition.add_value(self.element_1, 0)
         self.assertEqual(2, len(definition.get_values()))
-        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
-        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(1))
+        self.assertEqual(self.element_1, definition.get_value(0))
+        self.assertEqual(self.element_2, definition.get_value(1))
 
     def test_from_string(self):
         text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+            self.element_1, self.element_2)
         definition = DefinitionTree.from_string(text)
         self.assertEqual('test', definition.get_name())
         self.assertEqual('value', definition.get_attribute('key'))
         definitions = definition.get_values()
         self.assertEqual(2, len(definitions))
-        self.assertEqual(DefinitionTreeTest.element_1, definitions[0])
-        self.assertEqual(DefinitionTreeTest.element_2, definitions[1])
+        self.assertEqual(self.element_1, definitions[0])
+        self.assertEqual(self.element_2, definitions[1])
 
     def test_from_lxml(self):
         root = etree.Element('DEFINE')
         root.set('name', 'test')
         root.set('key', 'value')
-        root.append(DefinitionTreeTest.element_1.to_lxml_element())
-        root.append(DefinitionTreeTest.element_2.to_lxml_element())
+        root.append(self.element_1.to_lxml_element())
+        root.append(self.element_2.to_lxml_element())
         definition = DefinitionTree.from_lxml_element(root)
         self.assertEqual('test', definition.get_name())
         self.assertEqual('value', definition.get_attribute('key'))
         definitions = definition.get_values()
         self.assertEqual(2, len(definitions))
-        self.assertEqual(DefinitionTreeTest.element_1, definitions[0])
-        self.assertEqual(DefinitionTreeTest.element_2, definitions[1])
+        self.assertEqual(self.element_1, definitions[0])
+        self.assertEqual(self.element_2, definitions[1])
 
     def test_equality(self):
         text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+            self.element_1, self.element_2)
         self.assertEqual(DefinitionTree.from_string(text), 
DefinitionTree.from_string(text))
         self.assertNotEqual(DefinitionTree.from_string(text), 
DefinitionTree('test'))
 
     def test_to_string(self):
         text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+            self.element_1, self.element_2)
         definition = DefinitionTree.from_string(text)
         copy = DefinitionTree.from_string(str(definition))
         self.assertEqual(definition, copy)
 
     def test_to_lxml(self):
         text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
-            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+            self.element_1, self.element_2)
         definition = DefinitionTree.from_string(text)
         copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
         self.assertEqual(definition, copy)



commit fae84295c23e72dca20766fba304f030f882365f
Author: Marek Aaron Sapota <address@hidden>
Date:   Sat Jun 27 16:21:57 2009 +0200

    Added Definition, DefinitionElement and DefinitionTree classes.

diff --git a/misc/py_control_client/pycontrollib/definition.py 
b/misc/py_control_client/pycontrollib/definition.py
new file mode 100644
index 0000000..1d7e6c4
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/definition.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+class Definition():
+    '''Common interface for DefinitionElement and DefinitionTree. Objects of
+    this type should not exist alone.'''
+
+    def __init__(self, name = None, attributes = {}):
+        '''Creates new definition with given name and attributes.'''
+        self.name = name
+        self.attributes = attributes
+
+    def __eq__(self, other):
+        return self.name == other.name and self.attributes == other.attributes
+
+    def get_name(self):
+        '''Definition name, None means no name.'''
+        return self.name
+
+    def set_name(self, name):
+        '''Set definition name, None means no name.'''
+        self.name = name
+
+    def get_attribute(self, key):
+        '''Get value of attribute key.'''
+        return self.attributes.get(key)
+
+    def set_attribute(self, key, value):
+        '''Set attribute key to given value.'''
+        self.attributes[key] = value
+
+    def remove_attribute(self, key):
+        '''Remove attribute key.'''
+        self.attributes.pop(key)
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce definition element or tree from etree.Element
+        object.'''
+        if len(list(root)):
+            return DefinitionTree.from_lxml_element(root)
+        else:
+            return DefinitionElement.from_lxml_element(root)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce definition element or tree by parsing a 
string.'''
+        return Definition.from_lxml_element(etree.XML(text))
+
+class DefinitionElement(Definition):
+    '''Single definition element.'''
+
+    def __init__(self, name = None, attributes = {}):
+        '''Creates new definition element with given name and attributes.'''
+        Definition.__init__(self, name, attributes)
+
+    def __eq__(self, other):
+        return Definition.__eq__(self, other)
+
+    def to_lxml_element(self):
+        '''Convert definition element to etree.Element object.'''
+        root = etree.Element('DEFINE')
+        for key, value in self.attributes.iteritems():
+            root.set(key, value)
+        if self.name is not None:
+            root.set('name', self.name)
+        return root
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce definition element from etree.Element object.'''
+        attributes = root.attrib
+        name = attributes.pop('name', None)
+        return DefinitionElement(name, attributes)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce definition element by parsing a string.'''
+        return DefinitionElement.from_lxml_element(etree.XML(text))
+
+class DefinitionTree(Definition):
+    '''Definition element containing other definitions.'''
+
+    def __init__(self, name = None, values = [], attributes = {}):
+        '''Creates new definition tree with given name, sub-definitions and
+        attributes.'''
+        Definition.__init__(self, name, attributes)
+        self.values = values
+
+    def __eq__(self, other):
+        return Definition.__eq__(self, other) and self.values == other.values
+
+    def get_values(self):
+        '''Get all sub-definitions.'''
+        return self.values
+
+    def get_value(self, index):
+        '''Get sub-definition with given index.'''
+        return self.values[index]
+
+    def add_value(self, value, index = None):
+        '''Add value to sub-definitions, either at given position, or at the
+        end.'''
+        if index is None:
+            self.values.append(value)
+        else:
+            self.values.insert(index, value)
+
+    def remove_value(self, index):
+        '''Remove sub-definition with given index.'''
+        self.values.pop(index)
+
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce definition tree from etree.Element object.'''
+        attributes = root.attrib
+        name = attributes.pop('name', None)
+        values = map(Definition.from_lxml_element, list(root))
+        return DefinitionTree(name, values, attributes)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce definition tree by parsing a string.'''
+        return DefinitionTree.from_lxml_element(etree.XML(text))
+
+    def to_lxml_element(self):
+        '''Convert definition tree to etree.Element object.'''
+        root = etree.Element('DEFINE')
+        for key, value in self.attributes.iteritems():
+            root.set(key, value)
+        if self.name is not None:
+            root.set('name', self.name)
+        for value in self.values:
+            root.append(value.to_lxml_element())
+        return root
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
diff --git a/misc/py_control_client/pycontrollib/definition_test.py 
b/misc/py_control_client/pycontrollib/definition_test.py
new file mode 100644
index 0000000..d4b06f6
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/definition_test.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from definition import Definition, DefinitionElement, DefinitionTree
+from lxml import etree
+import unittest
+
+class DefinitionTest(unittest.TestCase):
+    def test_creation(self):
+        definition = Definition()
+        self.assertEqual(None, definition.get_name())
+        self.assertEqual({}, definition.attributes)
+        definition = Definition('no attributes')
+        self.assertEqual('no attributes', definition.get_name())
+        self.assertEqual({}, definition.attributes)
+        definition = Definition('with attributes', {'a': 'b', 'c': 'd'})
+        self.assertEqual('with attributes', definition.get_name())
+        self.assertEqual({'a': 'b', 'c': 'd'}, definition.attributes)
+
+    def test_name(self):
+        definition = Definition('name1')
+        self.assertEqual('name1', definition.get_name())
+        definition.set_name('name2')
+        self.assertEqual('name2', definition.get_name())
+
+    def test_attributes(self):
+        definition = Definition('with attributes', {'a': 'b', 'c': 'd'})
+        self.assertEqual('b', definition.get_attribute('a'))
+        self.assertEqual('d', definition.get_attribute('c'))
+        self.assertEqual(None, definition.get_attribute('e'))
+        definition.set_attribute('e', 'f')
+        self.assertEqual('f', definition.get_attribute('e'))
+        definition.set_attribute('e', 'g')
+        self.assertEqual('g', definition.get_attribute('e'))
+        definition.remove_attribute('e')
+        self.assertEqual(None, definition.get_attribute('e'))
+        self.assertRaises(KeyError, definition.remove_attribute, 'e')
+
+    def test_from_string(self):
+        text = '<DEFINE name="http.error.file.404" value="404.html" />'
+        definition = Definition.from_string(text)
+        self.assertTrue(isinstance(definition, DefinitionElement))
+        text = '<DEFINE name="x"><DEFINE value="y" /><DEFINE value="z" 
/></DEFINE>'
+        definition = Definition.from_string(text)
+        self.assertTrue(isinstance(definition, DefinitionTree))
+
+    def test_from_lxml(self):
+        text = '<DEFINE name="http.error.file.404" value="404.html" />'
+        definition = Definition.from_lxml_element(etree.XML(text))
+        self.assertTrue(isinstance(definition, DefinitionElement))
+        text = '<DEFINE name="x"><DEFINE value="y" /><DEFINE value="z" 
/></DEFINE>'
+        definition = Definition.from_lxml_element(etree.XML(text))
+        self.assertTrue(isinstance(definition, DefinitionTree))
+
+class DefinitionElementTest(unittest.TestCase):
+    def test_creation(self):
+        definition = DefinitionElement()
+        self.assertTrue(isinstance(definition, Definition))
+        definition = DefinitionElement('name')
+        self.assertTrue(isinstance(definition, Definition))
+        definition = DefinitionElement('name', {'value': 'value'})
+        self.assertTrue(isinstance(definition, Definition))
+
+    def test_from_string(self):
+        text = '<DEFINE name="http.error.file.404" value="404.html" />'
+        definition = DefinitionElement.from_string(text)
+        self.assertEqual('http.error.file.404', definition.get_name())
+        self.assertEqual('404.html', definition.get_attribute('value'))
+        text = '''<DEFINE server="/opt/bin/fastcgi_server" domain="fastcgi"
+host="localhost" port="2010" local="yes" uid="1000" gid="1000" />'''
+        definition = DefinitionElement.from_string(text)
+        self.assertEqual(None, definition.get_attribute('value'))
+        self.assertEqual(None, definition.get_name())
+        self.assertEqual('/opt/bin/fastcgi_server',
+                         definition.get_attribute('server'))
+        self.assertEqual('fastcgi', definition.get_attribute('domain'))
+        self.assertEqual('localhost', definition.get_attribute('host'))
+        self.assertEqual('2010', definition.get_attribute('port'))
+        self.assertEqual('yes', definition.get_attribute('local'))
+        self.assertEqual('1000', definition.get_attribute('uid'))
+        self.assertEqual('1000', definition.get_attribute('gid'))
+
+    def test_from_lxml(self):
+        root = etree.Element('DEFINE')
+        root.set('server', '/opt/bin/fastcgi_server')
+        root.set('domain', 'fastcgi')
+        root.set('host', 'localhost')
+        root.set('port', '2010')
+        root.set('local', 'yes')
+        root.set('uid', '1000')
+        root.set('gid', '1000')
+        definition = DefinitionElement.from_lxml_element(root)
+        self.assertEqual(None, definition.get_attribute('value'))
+        self.assertEqual(None, definition.get_name())
+        self.assertEqual('/opt/bin/fastcgi_server',
+                         definition.get_attribute('server'))
+        self.assertEqual('fastcgi', definition.get_attribute('domain'))
+        self.assertEqual('localhost', definition.get_attribute('host'))
+        self.assertEqual('2010', definition.get_attribute('port'))
+        self.assertEqual('yes', definition.get_attribute('local'))
+        self.assertEqual('1000', definition.get_attribute('uid'))
+        self.assertEqual('1000', definition.get_attribute('gid'))
+
+    def test_equality(self):
+        self.assertEqual(DefinitionElement('name', {'some': 'attributes'}),
+                         DefinitionElement('name', {'some': 'attributes'}))
+        self.assertNotEqual(DefinitionElement('name1'),
+                            DefinitionElement('name2'))
+        self.assertNotEqual(DefinitionElement('name', {'attribute': '1'}),
+                            DefinitionElement('name', {'attribute': '2'}))
+
+    def test_to_string(self):
+        text = '''<DEFINE server="/opt/bin/fastcgi_server" domain="fastcgi"
+host="localhost" port="2010" local="yes" uid="1000" gid="1000" />'''
+        definition = DefinitionElement.from_string(text)
+        copy = DefinitionElement.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_lxml(self):
+        text = '''<DEFINE name="test" server="/opt/bin/fastcgi_server"
+domain="fastcgi" host="localhost" port="2010" local="yes" uid="1000" gid="1000"
+/>'''
+        definition = DefinitionElement.from_string(text)
+        copy = 
DefinitionElement.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+class DefinitionTreeTest(unittest.TestCase):
+    element_1 = DefinitionElement('http.error.file.404', {'value': '404.html'})
+    element_2 = DefinitionElement(attributes = {'server': 
'/opt/bin/fastcgi_server',
+                                                'domain': 'fastcgi', 'host': 
'localhost',
+                                                'port': '2010', 'local': 'yes',
+                                                'uid': '1000', 'gid': '1000'})
+    def test_creation(self):
+        definition = DefinitionTree()
+        self.assertTrue(isinstance(definition, Definition))
+        definition = DefinitionTree('name')
+        self.assertTrue(isinstance(definition, Definition))
+        definition = DefinitionTree('name', [DefinitionTreeTest.element_1])
+        self.assertTrue(isinstance(definition, Definition))
+        definition = DefinitionTree('name', [DefinitionTreeTest.element_2], 
{'value': '1'})
+        self.assertTrue(isinstance(definition, Definition))
+
+    def test_values(self):
+        definition = DefinitionTree()
+        self.assertEqual(0, len(definition.get_values()))
+        definition.add_value(DefinitionTreeTest.element_1)
+        self.assertEqual(1, len(definition.get_values()))
+        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
+        definition.add_value(DefinitionTreeTest.element_2)
+        self.assertEqual(2, len(definition.get_values()))
+        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
+        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(1))
+        definition.remove_value(0)
+        self.assertEqual(1, len(definition.get_values()))
+        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(0))
+        definition.add_value(DefinitionTreeTest.element_1, 0)
+        self.assertEqual(2, len(definition.get_values()))
+        self.assertEqual(DefinitionTreeTest.element_1, definition.get_value(0))
+        self.assertEqual(DefinitionTreeTest.element_2, definition.get_value(1))
+
+    def test_from_string(self):
+        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
+            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+        definition = DefinitionTree.from_string(text)
+        self.assertEqual('test', definition.get_name())
+        self.assertEqual('value', definition.get_attribute('key'))
+        definitions = definition.get_values()
+        self.assertEqual(2, len(definitions))
+        self.assertEqual(DefinitionTreeTest.element_1, definitions[0])
+        self.assertEqual(DefinitionTreeTest.element_2, definitions[1])
+
+    def test_from_lxml(self):
+        root = etree.Element('DEFINE')
+        root.set('name', 'test')
+        root.set('key', 'value')
+        root.append(DefinitionTreeTest.element_1.to_lxml_element())
+        root.append(DefinitionTreeTest.element_2.to_lxml_element())
+        definition = DefinitionTree.from_lxml_element(root)
+        self.assertEqual('test', definition.get_name())
+        self.assertEqual('value', definition.get_attribute('key'))
+        definitions = definition.get_values()
+        self.assertEqual(2, len(definitions))
+        self.assertEqual(DefinitionTreeTest.element_1, definitions[0])
+        self.assertEqual(DefinitionTreeTest.element_2, definitions[1])
+
+    def test_equality(self):
+        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
+            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+        self.assertEqual(DefinitionTree.from_string(text), 
DefinitionTree.from_string(text))
+        self.assertNotEqual(DefinitionTree.from_string(text), 
DefinitionTree('test'))
+
+    def test_to_string(self):
+        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
+            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+        definition = DefinitionTree.from_string(text)
+        copy = DefinitionTree.from_string(str(definition))
+        self.assertEqual(definition, copy)
+
+    def test_to_lxml(self):
+        text = '<DEFINE name="test" key="value">{0}{1}</DEFINE>'.format(
+            DefinitionTreeTest.element_1, DefinitionTreeTest.element_2)
+        definition = DefinitionTree.from_string(text)
+        copy = DefinitionTree.from_lxml_element(definition.to_lxml_element())
+        self.assertEqual(definition, copy)
+
+if __name__ == '__main__':
+    unittest.main()



commit 6ad378bcc4489348696a846ae8f17b5c8f8455eb
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jun 26 23:09:31 2009 +0200

    Rename test.py to include module name.

diff --git a/misc/py_control_client/pycontrollib/test.py 
b/misc/py_control_client/pycontrollib/mimetypes_test.py
similarity index 100%
rename from misc/py_control_client/pycontrollib/test.py
rename to misc/py_control_client/pycontrollib/mimetypes_test.py



commit 6c4d700d2fc3a4e9174ebae276cd9e3bb5a4da2f
Author: Marek Aaron Sapota <address@hidden>
Date:   Fri Jun 26 12:36:59 2009 +0200

    Skeleton class for handling MIME types configuration.

diff --git a/misc/py_control_client/pycontrollib/mimetypes.py 
b/misc/py_control_client/pycontrollib/mimetypes.py
new file mode 100644
index 0000000..b9f4877
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/mimetypes.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+class MIMEType():
+    valid_handlers = set(['SEND', 'CGI', 'FASTCGI', 'SCGI', 'MSCGI', 'ISAPI',
+                          'WINCGI', 'PROxY'])
+
+    class WrongArguments(Exception):
+        '''Exception raised when MIMEType is constructed with invalid
+        arguments.'''
+        pass
+
+    def __init__(self, mime, handler, param, extension, path = None,
+                 filter = None, self_executed = None, definitions = None):
+        '''Creates new MIMEType with specified attributes.'''
+        self.mime = mime
+        if handler not in MIMEType.valid_handlers:
+            raise MIMEType.WrongArguments(
+                '{0} is not a valid handler'.format(handler))
+        self.handler = handler
+        self.param = param
+        self.extension = extension
+        self.path = path
+        self.filter = filter
+        self.self_executed = self_executed
+        self.definitions = definitions
+        
+    def __eq__(self, other):
+        return \
+            self.mime == other.mime and \
+            self.handler == other.handler and \
+            self.param == other.param and \
+            self.extension == other.extension and \
+            self.path == other.path and \
+            self.filter == other.filter and \
+            self.self_executed == other.self_executed and \
+            self.definitions == other.definitions
+
+    def to_lxml_element(self): 
+        def make_element(tag, attribute, value):
+            element = etree.Element(tag)
+            element.set(attribute, value)
+            return element
+        def make_extension_element(extension):
+            return make_element('EXTENSION', 'value', extension)
+        root = etree.Element('MIME')
+        root.set('mime', self.mime)
+        root.set('handler', self.handler)
+        root.set('param', self.param)
+        if self.self_executed is not None:
+            root.set('self', 'YES' if self.self_executed else 'NO')
+        for element in map(make_extension_element, self.extension):
+            root.append(element)
+        if self.path is not None:
+            root.append(make_element('PATH', 'regex', self.path))
+        if self.filter is not None:
+            root.append(make_element('FILTER', 'value', self.filter))
+        return root
+
+    def __str__(self): # TODO: definitions
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+
+    @staticmethod
+    def from_lxml_element(root): # TODO: definitions
+        '''Factory to produce MIMEType from etree.Element object.'''
+        mime = root.get('mime', None)
+        handler = root.get('handler', None)
+        param = root.get('param', None)
+        self_executed = root.get('self', None)
+        if self_executed is not None:
+            self_executed = False if self_executed == 'NO' else True
+        extension = set(map(lambda element: element.get('value'),
+                            root.findall('EXTENSION')))
+        path = root.find('PATH')
+        if path is not None:
+            path = path.get('regex')
+        filter = root.find('FILTER')
+        if filter is not None:
+            filter = filter.get('value')
+        return MIMEType(mime, handler, param, extension, path, filter,
+                        self_executed, None)
+
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce MIMEType by parsing a string.'''
+        return MIMEType.from_lxml_element(etree.XML(text))
+
+class MIMETypes():
+    def __init__(self, MIME_types):
+        self.MIME_types = MIME_types
+        
+    def __eq__(self, other):
+        return self.MIME_types == other.MIME_types
+
+    def to_lxml_element(self):
+        root = etree.Element('MIMES')
+        for mime in self.MIME_types:
+            root.append(mime.to_lxml_element())
+        return root
+
+    def __str__(self):
+        return etree.tostring(self.to_lxml_element(), pretty_print = True)
+        
+    @staticmethod
+    def from_lxml_element(root):
+        '''Factory to produce MIMETypes from etree.Element object.'''
+        return MIMETypes(map(MIMEType.from_lxml_element, root.findall('MIME')))
+    
+    @staticmethod
+    def from_string(text):
+        '''Factory to produce MIMETypes from parsing a string.'''
+        return MIMETypes.from_lxml_element(etree.XML(text))
diff --git a/misc/py_control_client/pycontrollib/test.py 
b/misc/py_control_client/pycontrollib/test.py
new file mode 100644
index 0000000..ec96779
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/test.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 unittest
+from mimetypes import MIMEType, MIMETypes
+
+class MIMETypeTest(unittest.TestCase):
+    text = '''<MIME mime="application/xhtml+xml" handler="CGI" 
param="/usr/bin/python" self="NO" >
+  <EXTENSION value="xhtml" />
+  <EXTENSION value="xml" />
+  <EXTENSION value="py" />
+  <FILTER value="gzip" />
+  <PATH regex="^/cgi-bin/python/.*$" />
+</MIME>'''
+    
+    def test_from_string(self):
+        mime = MIMEType.from_string(MIMETypeTest.text)
+        self.assertEqual(mime.mime, 'application/xhtml+xml')
+        self.assertEqual(mime.handler, 'CGI')
+        self.assertEqual(mime.param, '/usr/bin/python')
+        self.assertEqual(mime.extension, set(['xhtml', 'xml', 'py']))
+        self.assertEqual(mime.path, '^/cgi-bin/python/.*$')
+        self.assertEqual(mime.filter, 'gzip')
+        self.assertEqual(mime.self_executed, False)
+        
+    def test_to_string(self):
+        mime = MIMEType.from_string(MIMETypeTest.text)
+        copy = MIMEType.from_string(str(mime))
+        self.assertEqual(mime, copy)
+        
+class MIMETypesTest(unittest.TestCase):
+    mime_0 = '''<MIME mime="text/html" handler="FASTCGI" self="YES" param="">
+  <EXTENSION value="fcgi"/>
+</MIME>'''
+
+    mime_1 = '''<MIME mime="text/plain" handler="SEND" param="">
+  <EXTENSION value="asc"/>
+  <EXTENSION value="c"/>
+  <EXTENSION value="cc"/>
+  <EXTENSION value="f"/>
+  <EXTENSION value="f90"/>
+  <EXTENSION value="h"/>
+  <EXTENSION value="hh"/>
+  <EXTENSION value="m"/>
+  <EXTENSION value="txt"/>
+</MIME>'''
+    
+    text = '<MIMES>{0}{1}</MIMES>'.format(mime_0, mime_1)
+    
+    def test_from_string(self):
+        mimes = MIMETypes.from_string(MIMETypesTest.text)
+        self.assertEqual(mimes.MIME_types[0],
+                         MIMEType.from_string(MIMETypesTest.mime_0))
+        self.assertEqual(mimes.MIME_types[1],
+                         MIMEType.from_string(MIMETypesTest.mime_1))
+        
+    def test_to_string(self):
+        mimes = MIMETypes.from_string(MIMETypesTest.text)
+        copy = MIMETypes.from_string(str(mimes))
+        self.assertEqual(mimes, copy)
+        
+if __name__ == '__main__':
+    unittest.main()



commit 2e82879117b0d54b6c9a61519a3511e6e4be4136
Author: Marek Aaron Sapota <address@hidden>
Date:   Mon Jun 22 21:37:18 2009 +0200

    Added files needed for installation on unix systems.

diff --git a/myserver/binaries/MIMEtypes.xml.unix.default 
b/myserver/binaries/MIMEtypes.xml.unix.default
new file mode 100755
index 0000000..f6830f6
--- /dev/null
+++ b/myserver/binaries/MIMEtypes.xml.unix.default
@@ -0,0 +1,503 @@
+<?xml version="1.0"?>
+
+<MIMES>
+  <MIME mime="text/html" handler="MSCGI" param="">
+    <EXTENSION value="mscgi"/>
+  </MIME>
+  
+  <MIME mime="text/html" handler="CGI" self="YES" param="">
+    <EXTENSION value="cgi"/>
+  </MIME>
+  
+  <MIME mime="text/html" handler="ISAPI" param="">
+    <EXTENSION value="isapi"/>
+  </MIME>
+  
+  <MIME mime="text/html" handler="SCGI" self="YES" param="">
+    <EXTENSION value="scgi"/>
+  </MIME>
+
+  <MIME mime="text/html" handler="FASTCGI" self="YES" param="">
+    <EXTENSION value="fcgi"/>
+  </MIME>
+  
+  <MIME mime="text/html" handler="WINCGI" param="">
+    <EXTENSION value="wincgi"/>
+  </MIME>
+
+
+  <MIME mime="application/postscript" handler="SEND" param="">
+    <EXTENSION value="ai"/>
+    <EXTENSION value="eps"/>
+    <EXTENSION value="ps"/>
+  </MIME>
+  
+  <MIME mime="video/quicktime" handler="SEND" param="">
+    <EXTENSION value="mov"/>
+    <EXTENSION value="qt"/>
+  </MIME>
+
+  <MIME mime="image/png" handler="SEND" param="">
+    <EXTENSION value="png"/>
+  </MIME>
+
+  <MIME mime="video/vndvivo" handler="SEND" param="">
+    <EXTENSION value="viv"/>
+    <EXTENSION value="vivo"/>
+  </MIME>
+
+  <MIME mime="application/xtex" handler="SEND" param="">
+    <EXTENSION value="tex"/>
+  </MIME>
+
+  <MIME mime="text/xsetext" handler="SEND" param="">
+    <EXTENSION value="etx"/>
+  </MIME>
+
+  <MIME mime="application/vda" handler="SEND" param="">
+    <EXTENSION value="vda"/>
+  </MIME>
+
+  <MIME mime="application/dsptype" handler="SEND" param="">
+    <EXTENSION value="tsp"/>
+  </MIME>
+
+  <MIME mime="application/xstuffit" handler="SEND" param="">
+    <EXTENSION value="sit"/>
+  </MIME>
+
+  <MIME mime="application/solids" handler="SEND" param="">
+    <EXTENSION value="sol"/>
+  </MIME>
+
+  <MIME mime="application/xfuturesplash" handler="SEND" param="">
+    <EXTENSION value="spl"/>
+  </MIME>
+
+  <MIME mime="application/xbcpio" handler="SEND" param="">
+    <EXTENSION value="bcpio"/>
+  </MIME>
+
+  <MIME mime="application/oda" handler="SEND" param="">
+    <EXTENSION value="oda"/>
+  </MIME>
+
+  <MIME mime="application/pro_eng" handler="SEND" param="">
+    <EXTENSION value="prt"/>
+  </MIME>
+
+  <MIME mime="model/iges" handler="SEND" param="">
+    <EXTENSION value="iges"/>
+    <EXTENSION value="igs"/>
+  </MIME>
+
+  <MIME mime="audio/TSPaudio" handler="SEND" param="">
+    <EXTENSION value="tsi"/>
+  </MIME>
+
+  <MIME mime="image/xportablegraymap" handler="SEND" param="">
+    <EXTENSION value="pgm"/>
+  </MIME>
+
+  <MIME mime="application/xlatex" handler="SEND" param="">
+    <EXTENSION value="latex"/>
+  </MIME>
+
+  <MIME mime="application/xsh" handler="SEND" param="">
+    <EXTENSION value="sh"/>
+  </MIME>
+
+  <MIME mime="application/drafting" handler="SEND" param="">
+    <EXTENSION value="drw"/>
+  </MIME>
+
+  <MIME mime="text/html" handler="SEND" param="">
+    <EXTENSION value="htm"/>
+    <EXTENSION value="html"/>
+  </MIME>
+
+  <MIME mime="application/smil" handler="SEND" param="">
+    <EXTENSION value="smi"/>
+    <EXTENSION value="smil"/>
+  </MIME>
+
+  <MIME mime="application/vndmif" handler="SEND" param="">
+    <EXTENSION value="mif"/>
+  </MIME>
+
+  <MIME mime="application/xipix" handler="SEND" param="">
+    <EXTENSION value="ipx"/>
+  </MIME>
+
+  <MIME mime="model/mesh" handler="SEND" param="">
+    <EXTENSION value="mesh"/>
+    <EXTENSION value="msh"/>
+    <EXTENSION value="silo"/>
+  </MIME>
+
+  <MIME mime="application/xtroffman" handler="SEND" param="">
+    <EXTENSION value="man"/>
+  </MIME>
+
+  <MIME mime="application/xcsh" handler="SEND" param="">
+    <EXTENSION value="csh"/>
+  </MIME>
+
+  <MIME mime="audio/xpnrealaudioplugin" handler="SEND" param="">
+    <EXTENSION value="rpm"/>
+  </MIME>
+
+  <MIME mime="image/cmuraster" handler="SEND" param="">
+    <EXTENSION value="ras"/>
+  </MIME>
+
+  <MIME mime="www/mime" handler="SEND" param="">
+    <EXTENSION value="mime"/>
+  </MIME>
+
+  <MIME mime="application/xdirector" handler="SEND" param="">
+    <EXTENSION value="dcr"/>
+    <EXTENSION value="dir"/>
+    <EXTENSION value="dxr"/>
+  </MIME>
+
+  <MIME mime="text/xml" handler="SEND" param="">
+    <EXTENSION value="xml"/>
+  </MIME>
+
+  <MIME mime="application/xfreelance" handler="SEND" param="">
+    <EXTENSION value="pre"/>
+  </MIME>
+
+  <MIME mime="image/ief" handler="SEND" param="">
+    <EXTENSION value="ief"/>
+  </MIME>
+
+  <MIME mime="application/xshar" handler="SEND" param="">
+    <EXTENSION value="shar"/>
+  </MIME>
+
+  <MIME mime="audio/xrealaudio" handler="SEND" param="">
+    <EXTENSION value="ra"/>
+  </MIME>
+
+  <MIME mime="application/mspowerpoint" handler="SEND" param="">
+    <EXTENSION value="pot"/>
+    <EXTENSION value="pps"/>
+    <EXTENSION value="ppt"/>
+    <EXTENSION value="ppz"/>
+  </MIME>
+
+  <MIME mime="audio/midi" handler="SEND" param="">
+    <EXTENSION value="kar"/>
+    <EXTENSION value="mid"/>
+    <EXTENSION value="midi"/>
+  </MIME>
+
+  <MIME mime="image/jpeg" handler="SEND" param="">
+    <EXTENSION value="jpe"/>
+    <EXTENSION value="jpeg"/>
+    <EXTENSION value="jpg"/>
+  </MIME>
+
+  <MIME mime="application/xkoan" handler="SEND" param="">
+    <EXTENSION value="skd"/>
+    <EXTENSION value="skm"/>
+    <EXTENSION value="skp"/>
+    <EXTENSION value="skt"/>
+  </MIME>
+
+  <MIME mime="application/SLA" handler="SEND" param="">
+    <EXTENSION value="stl"/>
+  </MIME>
+
+  <MIME mime="application/STEP" handler="SEND" param="">
+    <EXTENSION value="step"/>
+    <EXTENSION value="stp"/>
+  </MIME>
+
+  <MIME mime="application/xshockwaveflash" handler="SEND" param="">
+    <EXTENSION value="swf"/>
+  </MIME>
+
+  <MIME mime="image/xxwindowdump" handler="SEND" param="">
+    <EXTENSION value="xwd"/>
+  </MIME>
+
+  <MIME mime="image/xxbitmap" handler="SEND" param="">
+    <EXTENSION value="xbm"/>
+  </MIME>
+
+  <MIME mime="application/dxf" handler="SEND" param="">
+    <EXTENSION value="dxf"/>
+  </MIME>
+
+  <MIME mime="text/sgml" handler="SEND" param="">
+    <EXTENSION value="sgm"/>
+    <EXTENSION value="sgml"/>
+  </MIME>
+
+  <MIME mime="model/vrml" handler="SEND" param="">
+    <EXTENSION value="vrml"/>
+    <EXTENSION value="wrl"/>
+  </MIME>
+
+  <MIME mime="application/xipscript" handler="SEND" param="">
+    <EXTENSION value="ips"/>
+  </MIME>
+
+  <MIME mime="image/xportablepixmap" handler="SEND" param="">
+    <EXTENSION value="ppm"/>
+  </MIME>
+
+  <MIME mime="text/plain" handler="SEND" param="">
+    <EXTENSION value="asc"/>
+    <EXTENSION value="c"/>
+    <EXTENSION value="cc"/>
+    <EXTENSION value="f"/>
+    <EXTENSION value="f90"/>
+    <EXTENSION value="h"/>
+    <EXTENSION value="hh"/>
+    <EXTENSION value="m"/>
+    <EXTENSION value="txt"/>
+  </MIME>
+
+  <MIME mime="application/set" handler="SEND" param="">
+    <EXTENSION value="set"/>
+  </MIME>
+
+  <MIME mime="video/xmsvideo" handler="SEND" param="">
+    <EXTENSION value="avi"/>
+  </MIME>
+
+  <MIME mime="audio/xwav" handler="SEND" param="">
+    <EXTENSION value="wav"/>
+  </MIME>
+
+  <MIME mime="application/xgtar" handler="SEND" param="">
+    <EXTENSION value="gtar"/>
+  </MIME>
+
+  <MIME mime="application/xwaissource" handler="SEND" param="">
+    <EXTENSION value="src"/>
+  </MIME>
+
+  <MIME mime="application/acad" handler="SEND" param="">
+    <EXTENSION value="dwg"/>
+  </MIME>
+
+  <MIME mime="application/xlisp" handler="SEND" param="">
+    <EXTENSION value="lsp"/>
+  </MIME>
+
+  <MIME mime="application/zip" handler="SEND" param="">
+    <EXTENSION value="zip"/>
+  </MIME>
+
+  <MIME mime="audio/xpnrealaudio" handler="SEND" param="">
+    <EXTENSION value="ram"/>
+    <EXTENSION value="rm"/>
+  </MIME>
+
+  <MIME mime="text/tabseparatedvalues" handler="SEND" param="">
+    <EXTENSION value="tsv"/>
+  </MIME>
+
+  <MIME mime="image/xxpixmap" handler="SEND" param="">
+    <EXTENSION value="xpm"/>
+  </MIME>
+
+  <MIME mime="application/xtexinfo" handler="SEND" param="">
+    <EXTENSION value="texi"/>
+    <EXTENSION value="texinfo"/>
+  </MIME>
+
+  <MIME mime="application/xustar" handler="SEND" param="">
+    <EXTENSION value="ustar"/>
+  </MIME>
+
+  <MIME mime="application/xcpio" handler="SEND" param="">
+    <EXTENSION value="cpio"/>
+  </MIME>
+
+  <MIME mime="application/vndmsexcel" handler="SEND" param="">
+    <EXTENSION value="xlc"/>
+    <EXTENSION value="xll"/>
+    <EXTENSION value="xlm"/>
+    <EXTENSION value="xls"/>
+    <EXTENSION value="xlw"/>
+  </MIME>
+
+  <MIME mime="image/xrgb" handler="SEND" param="">
+    <EXTENSION value="rgb"/>
+  </MIME>
+
+  <MIME mime="image/xportablebitmap" handler="SEND" param="">
+    <EXTENSION value="pbm"/>
+  </MIME>
+
+  <MIME mime="application/pdf" handler="SEND" param="">
+    <EXTENSION value="pdf"/>
+  </MIME>
+
+  <MIME mime="application/xjavascript" handler="SEND" param="">
+    <EXTENSION value="js"/>
+  </MIME>
+
+  <MIME mime="application/xcdlink" handler="SEND" param="">
+    <EXTENSION value="vcd"/>
+  </MIME>
+
+  <MIME mime="chemical/xpdb" handler="SEND" param="">
+    <EXTENSION value="pdb"/>
+    <EXTENSION value="xyz"/>
+  </MIME>
+
+  <MIME mime="application/xsv4crc" handler="SEND" param="">
+    <EXTENSION value="sv4crc"/>
+  </MIME>
+
+  <MIME mime="image/gif" handler="SEND" param="">
+    <EXTENSION value="gif"/>
+  </MIME>
+
+  <MIME mime="application/xlotusscreencam" handler="SEND" param="">
+    <EXTENSION value="scm"/>
+  </MIME>
+
+  <MIME mime="image/tiff" handler="SEND" param="">
+    <EXTENSION value="tif"/>
+    <EXTENSION value="tiff"/>
+  </MIME>
+
+  <MIME mime="application/maccompactpro" handler="SEND" param="">
+    <EXTENSION value="cpt"/>
+  </MIME>
+
+  <MIME mime="application/clariscad" handler="SEND" param="">
+    <EXTENSION value="ccad"/>
+  </MIME>
+
+  <MIME mime="video/mpeg" handler="SEND" param="">
+    <EXTENSION value="mpe"/>
+    <EXTENSION value="mpeg"/>
+    <EXTENSION value="mpg"/>
+  </MIME>
+
+  <MIME mime="application/xtcl" handler="SEND" param="">
+    <EXTENSION value="tcl"/>
+  </MIME>
+
+  <MIME mime="application/xhdf" handler="SEND" param="">
+    <EXTENSION value="hdf"/>
+  </MIME>
+
+  <MIME mime="text/css" handler="SEND" param="">
+    <EXTENSION value="css"/>
+  </MIME>
+
+  <MIME mime="application/xtroff" handler="SEND" param="">
+    <EXTENSION value="roff"/>
+    <EXTENSION value="t"/>
+    <EXTENSION value="tr"/>
+  </MIME>
+
+  <MIME mime="application/macbinhex40" handler="SEND" param="">
+    <EXTENSION value="hqx"/>
+  </MIME>
+
+  <MIME mime="application/ideas" handler="SEND" param="">
+    <EXTENSION value="unv"/>
+  </MIME>
+
+  <MIME mime="audio/xaiff" handler="SEND" param="">
+    <EXTENSION value="aif"/>
+    <EXTENSION value="aifc"/>
+    <EXTENSION value="aiff"/>
+  </MIME>
+
+  <MIME mime="application/xdvi" handler="SEND" param="">
+    <EXTENSION value="dvi"/>
+  </MIME>
+
+  <MIME mime="application/andrewinset" handler="SEND" param="">
+    <EXTENSION value="ez"/>
+  </MIME>
+
+  <MIME mime="application/xnetcdf" handler="SEND" param="">
+    <EXTENSION value="cdf"/>
+    <EXTENSION value="nc"/>
+  </MIME>
+
+  <MIME mime="application/xsv4cpio" handler="SEND" param="">
+    <EXTENSION value="sv4cpio"/>
+  </MIME>
+
+  <MIME mime="video/xsgimovie" handler="SEND" param="">
+    <EXTENSION value="movie"/>
+  </MIME>
+
+  <MIME mime="audio/mpeg" handler="SEND" param="">
+    <EXTENSION value="mp2"/>
+    <EXTENSION value="mp3"/>
+    <EXTENSION value="mpga"/>
+  </MIME>
+
+  <MIME mime="application/xtroffme" handler="SEND" param="">
+    <EXTENSION value="me"/>
+  </MIME>
+
+  <MIME mime="xconference/xcooltalk" handler="SEND" param="">
+    <EXTENSION value="ice"/>
+  </MIME>
+
+  <MIME mime="application/octetstream" handler="SEND" param="">
+    <EXTENSION value="bin"/>
+    <EXTENSION value="class"/>
+    <EXTENSION value="dms"/>
+    <EXTENSION value="exe"/>
+    <EXTENSION value="lha"/>
+    <EXTENSION value="lzh"/>
+  </MIME>
+
+  <MIME mime="application/xtroffms" handler="SEND" param="">
+    <EXTENSION value="ms"/>
+  </MIME>
+
+  <MIME mime="application/xgzip" handler="SEND" param="">
+    <EXTENSION value="gz"/>
+  </MIME>
+
+  <MIME mime="audio/basic" handler="SEND" param="">
+    <EXTENSION value="au"/>
+    <EXTENSION value="snd"/>
+  </MIME>
+
+  <MIME mime="application/xtar" handler="SEND" param="">
+    <EXTENSION value="tar"/>
+  </MIME>
+
+  <MIME mime="text/rtf" handler="SEND" param="">
+    <EXTENSION value="rtf"/>
+  </MIME>
+
+  <MIME mime="text/richtext" handler="SEND" param="">
+    <EXTENSION value="rtx"/>
+  </MIME>
+
+  <MIME mime="image/xportableanymap" handler="SEND" param="">
+    <EXTENSION value="pnm"/>
+  </MIME>
+
+  <MIME mime="application/xchesspgn" handler="SEND" param="">
+    <EXTENSION value="pgn"/>
+  </MIME>
+
+  <MIME mime="video/xfli" handler="SEND" param="">
+    <EXTENSION value="fli"/>
+  </MIME>
+
+  <MIME mime="application/msword" handler="SEND" param="">
+    <EXTENSION value="doc"/>
+  </MIME>
+</MIMES>
diff --git a/myserver/binaries/myserver.xml.unix.default 
b/myserver/binaries/myserver.xml.unix.default
new file mode 100755
index 0000000..77f11ad
--- /dev/null
+++ b/myserver/binaries/myserver.xml.unix.default
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+
+<!--MODIFY THE FILE ONLY IF YOU ARE SURE OF WHAT YOU ARE DOING-->
+<MYSERVER>
+
+       <!--CHOOSE THE LANGUAGE TO USE FOR THE SERVER-->
+       <DEFINE name="server.language" value="english.xml" />
+
+       <!--VERBOSITY ON LOG FILE-->
+       <DEFINE name="server.verbosity" value="2" />
+
+       <!--NUMBER OF SERVING THREADS ALWAYS ACTIVE-->
+  <DEFINE name="server.static_threads" value="5" />
+
+       <!--MAXIMUM NUMBER OF SERVING THREADS THAT THE SCHEDULER CAN
+                       CREATE-->
+       <DEFINE name="server.max_threads" value="200" />
+
+       <!--DIMENSION OF EVERY BUFFER IN BYTES IF ON THE MACHINE THERE IS A
+                       LOT OF RAM      THIS VALUES CAN BE INCREASED TO MAKE 
MYSERVER
+                       FASTER. NORMALLY THE MEMORY USED BY BUFFERS IS EQUAL TO
+                       BUFFER_SIZE * 2 * NUM_OF_THREADS-->
+       <DEFINE name="server.buffer_size" value="102400" />
+
+       <!--DEFAULT FILENAME TO SEND IN A DIRECTORY
+                       IF THE FILE ISN'T IN THE PATH THEN THE DIRECTORY
+                       CONTENT IS SENT -->
+  <DEFINE name="http.default_file">
+    <DEFINE value="default.html"/>
+    <DEFINE value="default.htm"/>
+    <DEFINE value="index.html"/>
+    <DEFINE value="index.htm"/>
+  </DEFINE>
+
+       <!--TIMEOUT OF EVERY CLIENTS CONNECTED TO THE SERVER.
+                       IF THE CLIENT DOESN'T REQUEST ANYTHING FOR N
+                       SECONDS THE CONNECTION WITH THE CLIENT IS
+                       CLOSED. SET THIS TO 0 IF YOU DON'T WANT USE
+                       KEEP-ALIVE CONNECTIONS  -->
+       <DEFINE name="connection.timeout" value="180" />
+
+       <!--SET THIS TO YES FOR USE PERSONALIZED PAGES TO SEND MESSAGE,
+                       PERSONALIZED PAGES ARE IN THE SYSTEM DIRECTORY  -->
+       <DEFINE name="http.use_error_file" value="YES" />
+
+       <!--DEFINE THE MAX NUMBER OF CONNECTIONS TO ALLOW TO THE SERVER. IF
+                       0 THEN INFINITE CONNECTIONS ARE ALLOWED.-->
+       <DEFINE name="server.max_connections" value="0" />
+
+       <!--DEFINE THE MAX NUMBER OF CONNECTIONS TO ACCEPT.-->
+       <DEFINE name="server.max_accepted_connections" value="0" />
+
+  <!--TEXT AND BACKGROUND ATTRIBUTES FOR CONSOLE LOG MESSAGES. -->
+  <DEFINE name="log_color.info_fg" value="white" />
+  <DEFINE name="log_color.info_bg" value="black" />
+
+  <DEFINE name="log_color.warning_fg" value="yellow" />
+  <DEFINE name="log_color.warning_bg" value="black" />
+
+  <DEFINE name="log_color.error_fg" value="red" />
+  <DEFINE name="log_color.error_bg" value="black" />
+
+       <!--MAX SIZE OF THE LOG FILE IN BYTES   -->
+       <DEFINE name="server.max_log_size" value="1048576" />
+
+       <!--DEFINE THE FOLDER BROWSING STYLE-->
+       <DEFINE name="http.dir.css" value="/sys/css/browsestyle.css" />
+
+       <!--DEFINE THE ADMINISTRATOR E-MAIL-->
+       <DEFINE name="server.admin" value="address@hidden" />
+
+       <!--DEFINE THE GZIP COMPRESSION THRESHOLD VALUE-->
+       <DEFINE name="gzip.threshold" value="1048576" />
+
+       <!--DEFINE IF LINKS SHOULD BE FOLLOWED-->
+       <DEFINE name="symlinks.follow" value="YES" />
+
+       <!--MAX CACHE SIZE FOR STATIC FILES-->
+       <DEFINE name="server.max_files_cache" value="10485760" />
+
+       <!--MAX CACHE SIZE FOR SINGLE STATIC FILE-->
+       <DEFINE name="server.max_file_cache" value="1048576" />
+
+       <!--MIN CACHE SIZE FOR SINGLE STATIC FILE-->
+       <DEFINE name="server.min_file_cache" value="1048576" />
+
+       <!-- ENABLE HOME DIRECTORY PER USER -->
+       <DEFINE name="http.use_home_directory" value="NO" />
+
+       <!-- DEFINE NAME FOR HOME DIRECTORY BROWSING, BY DEFAULT THE SERVER
+                        USES public_html -->
+       <DEFINE name="http.home_directory" value="public_html" />
+
+  <!--SET THIS TO YES IF YOU WANT TO ENABLE THE CONTROL PROTOCOL-->
+       <DEFINE name="control.enabled" value="NO" />
+
+       <!--MAXIMUM NUMBER OF EXTERNAL SERVERS THAT CAN BE EXECUTED-->
+       <DEFINE name="server.max_servers" value="100" />
+
+  <!--SET HERE THE NAME FOR THE CONTROL ADMIN. WE SUGGEST YOU YO USE
+      SOMETHING DIFFERENT FROM ADMIN, ADMINISTRATOR OR ROOT-->
+  <DEFINE name="control.admin" value="ADMIN" />
+
+  <!--DEFINE HERE A GOOD STRONG PASSWORD FOR THE ADMIN-->
+  <DEFINE name="control.password" value="PLEASE_CHANGE_THIS_PASSWORD" />
+
+  <!--FTP GLOBAL CONFIGURATION-->
+  <!--ALLOW ANONYMOUS LOGIN-->
+  <DEFINE name="ftp.allow_anonymous" value="NO" />
+
+  <!--IF ANONYMOUS ALLOWED, TELLS IF NEEDS PASS-->
+  <DEFINE name="ftp.anonymous_need_pass" value="NO" />
+
+  <!--ALLOW ASYNCHRONOUS CMDS LIKE Abor, Quit, Stat-->
+  <DEFINE name="ftp.allow_asynchronous_cmds" value="YES" />
+
+  <!--ALLOW CLIENTS TO SEND MORE THAN ONE COMMAND TO SERVER INTO ONE REQUEST-->
+  <DEFINE name="ftp.allow_pipelining" value="NO" />
+
+  <!--ALLOW CLIENTS TO CHANGE SERVER FILES-->
+  <DEFINE name="ftp.allow_store" value="NO" />
+</MYSERVER>
diff --git a/myserver/binaries/virtualhosts.xml.unix.default 
b/myserver/binaries/virtualhosts.xml.unix.default
new file mode 100755
index 0000000..3d1897b
--- /dev/null
+++ b/myserver/binaries/virtualhosts.xml.unix.default
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+
+<VHOSTS>
+
+       <VHOST>
+               <NAME>Every HTTP connection</NAME>
+               
+               <PORT>80</PORT>
+               <PROTOCOL>HTTP</PROTOCOL>
+               
+               <DOCROOT>web</DOCROOT>
+               <SYSFOLDER>system</SYSFOLDER>
+
+               <ACCESSLOG>
+                  <STREAM location="file://logs/MyServerHTTP.log" 
cycle="1048576"/>
+                </ACCESSLOG>
+
+                <WARNINGLOG>
+                  <STREAM location="file://logs/MyServerHTTP.err" 
cycle="1048576"/>
+                </WARNINGLOG>
+       </VHOST>
+
+       <VHOST>
+               <NAME>FTP connection</NAME>
+               
+               <PORT>21</PORT>
+               <PROTOCOL>FTP</PROTOCOL>
+               
+               <DOCROOT>web</DOCROOT>
+               <SYSFOLDER>system</SYSFOLDER>
+               
+               <ACCESSLOG>
+                  <STREAM location="file://logs/MyServerFTP.log" 
cycle="1048576"/>
+                </ACCESSLOG>
+
+               <WARNINGLOG>
+                  <STREAM location="file://logs/MyServerFTP.err" 
cycle="1048576"/>
+                </WARNINGLOG>
+       </VHOST>
+</VHOSTS>



commit 055e94570934d1e39f40552d0af4c762f57dbdeb
Merge: 2123234 55be25f
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jun 16 23:35:36 2009 +0200

    Merge branch 'master' of git://git.savannah.gnu.org/myserver




commit 2123234285a7d3c72911f42f5b270e4a91bc149e
Author: Marek Aaron Sapota <address@hidden>
Date:   Tue Jun 16 12:10:23 2009 +0200

    Imported pycontrollib to main tree.

diff --git a/misc/py_control_client/__init__.py 
b/misc/py_control_client/__init__.py
new file mode 100644
index 0000000..8fa2136
--- /dev/null
+++ b/misc/py_control_client/__init__.py
@@ -0,0 +1,2 @@
+#
+
diff --git a/misc/py_control_client/pycontrollib/__init__.py 
b/misc/py_control_client/pycontrollib/__init__.py
new file mode 100644
index 0000000..c671cf0
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from pycontrollib import Controller
+import configWrapper
diff --git a/misc/py_control_client/pycontrollib/configWrapper.py 
b/misc/py_control_client/pycontrollib/configWrapper.py
new file mode 100644
index 0000000..63248e2
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/configWrapper.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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/>.
+'''
+
+from lxml import etree
+
+def make_vhost_list(data):
+    '''Make a list of VHostWrapper objects from data in format as
+    virtualhosts.xml configuration file.'''
+    parser = etree.XMLParser(remove_blank_text = True)
+    tree = etree.XML(data, parser)
+    return [VHostWrapper(host, from_text = False) for host in \
+        tree.findall('VHOST')]
+
+class VHostWrapper():
+    '''Class wrapping around vhost configuration.'''
+
+    allowed_protocols = set(['CONTROL', 'FTP', 'HTTP', 'HTTPS'])
+    
+    class ProtocolError(ValueError):
+        '''Exception raised when user tried to use invalid protocol.'''
+        def __init__(self, protocol):
+            '''protocol is the invalid protocol that user tried to use.'''
+            ValueError.__init__(self)
+            self.protocol = protocol
+        
+        def __str__(self):
+            return self.protocol + ' is not a valid protocol'
+
+    def __init__(self, data, from_text = True):
+        '''Create new VHostWrapper by parsing data, if from_text is False, then
+        it is assumed that data has already been parsed and is an lxml
+        object.'''
+        if from_text:
+            parser = etree.XMLParser(remove_blank_text = True)
+            self.tree = etree.XML(data, parser)
+        else:
+            self.tree = data
+        
+    def __str__(self):
+        return self.to_xml()
+
+    def to_xml(self):
+        '''Return configuration representation in xml format.'''
+        return etree.tostring(self.tree, encoding = 'utf-8',
+            xml_declaration = True, pretty_print = True)
+
+    def get_node(self, name, multiple = False):
+        '''Get value stored in node of given name. If multiple is True, returns
+        list of all values stored in nodes of given name'''
+        if not multiple:
+            node = self.tree.find(name)
+            if node is not None:
+                return node.text
+            return None
+        else:
+            nodes = self.tree.findal(name)
+            return map(lambda node: node.text, nodes)
+
+    def set_node(self, name, data):
+        '''Set value stored in node of given name to data. Don't use it when
+        multiple nodes with given name are present, use add_node and del_node
+        instead.'''
+        node = self.tree.find(name)
+        if data is None:
+            if node is not None:
+                self.tree.remove(node)
+            return
+        if node is not None:
+            node.text = data
+        else:
+            node = etree.Element(name)
+            node.text = data
+            self.tree.append(node)
+            
+    def add_node(self, name, data):
+        '''Add new node with given name and value to tree.'''
+        node = self.tree.makeelement(name)
+        node.text = data
+        self.tree.append(node)
+        
+    def del_node(self, name, data):
+        '''Remove node with given name and value from tree.'''
+        for node in self.tree.findall(name):
+            if node.text == data:
+                self.tree.remove(node)
+                break
+
+    def get_name(self):
+        '''Get vhost name.'''
+        return self.get_node('NAME')
+
+    def set_name(self, name):
+        '''Set vhost name.'''
+        self.set_node('NAME', name)
+
+    def get_port(self):
+        '''Get port used to accept connections.'''
+        return self.get_node('PORT')
+
+    def set_port(self, port):
+        '''Set port used to accept connections.'''
+        self.set_node('PORT', str(port))
+
+    def get_ip(self):
+        '''Get list of CIDR subnet masks for this vhost.'''
+        return self.get_node('IP', multiple = True)
+
+    def add_ip(self, ip):
+        '''Add CIDR subnet mask for this vhost.'''
+        self.add_node('IP', ip)
+        
+    def del_ip(self, ip):
+        '''Remove CIDR subnet mast for this vhost.'''
+        self.del_node('IP', ip)
+
+    def get_protocol(self):
+        '''Get used protocol.'''
+        return self.get_node('PROTOCOL')
+
+    def set_protocol(self, protocol):
+        '''Set used protocol. Will raise ProtocolError if there is no such
+        protocol.'''
+        if protocol not in self.allowed_protocols:
+            raise self.ProtocolError(protocol)
+        self.set_node('PROTOCOL', protocol)
+
+    def get_docroot(self):
+        '''Get document root directory.'''
+        return self.get_node('DOCROOT')
+
+    def set_docroot(self, docroot):
+        '''Set document root directory.'''
+        self.set_node('DOCROOT', docroot)
+
+    def get_sysfolder(self):
+        '''Get system directory.'''
+        return self.get_node('SYSFOLDER')
+
+    def set_sysfolder(self, sysfolder):
+        '''Set system directory.'''
+        self.set_node('SYSFOLDER', sysfolder)
+
+    def get_host(self): # TODO: useRegex option
+        '''Get list of host names.'''
+        return self.get_node('HOST', multiple = True)
+
+    def add_host(self, host): # TODO: useRegex option
+        '''Add host name.'''
+        self.add_node('HOST', host)
+
+    def del_host(self, host): # TODO: useRegex option
+        '''Remove host name.'''
+        self.del_node('HOST', host)
+
+    def get_accesslog(self): # TODO: streams, etc.
+        '''Get access log used by this vhost.'''
+        return self.get_node('ACCESSLOG')
+
+    def set_accesslog(self, accesslog): # TODO: streams, etc.
+        '''Set access log used by this vhost.'''
+        self.set_node('ACCESSLOG', accesslog)
+
+    def get_warninglog(self): # TODO: streams, etc.
+        '''Get errors log used by this vhost.'''
+        return self.get_node('WARNINGLOG')
+
+    def set_warninglog(self, warninglog): # TODO: streams, etc.
+        '''Set errors log used by this vhost.'''
+        self.set_node('WARNINGLOG', warninglog)
+
diff --git a/misc/py_control_client/pycontrollib/pycontrollib.py 
b/misc/py_control_client/pycontrollib/pycontrollib.py
new file mode 100644
index 0000000..a32133c
--- /dev/null
+++ b/misc/py_control_client/pycontrollib/pycontrollib.py
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+'''
+MyServer
+Copyright (C) 2009 Free Software Foundation, Inc.
+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 socket
+from myserver.pycontrol import PyMyServerControl
+from configWrapper import VHostWrapper, make_vhost_list
+
+import sys
+
+class Controller():
+    '''High level library to control GNU MyServer. Every method returns True
+    or requested data on success and False on error.'''
+    class Parser():
+        '''Parse command output.'''
+        def connections(self, lines): # TODO: split lines to dicts
+            '''Parse data returned by SHOWCONNECTIONS command.'''
+            l = []
+            for line in lines.splitlines():
+                parts = line.split(' - ')
+                l.append(dict(zip(['id', 'client_ip', 'client_port',
+                    'server_ip', 'server_port', '?a', '?b'], parts)))
+            return l
+
+        def language_files(self, lines):
+            '''Parse data returned by SHOWLANGUAGEFILES command.'''
+            return lines.splitlines()
+
+    def __init__(self, host, port, username, password):
+        self.connection = None
+        self.parser = self.Parser()
+        self.host = host
+        self.port = port
+        self.username = username
+        self.password = password
+        self.response_code = 100
+
+    def get_response_code(self):
+        '''Get response code of last issued command.'''
+        return self.response_code
+
+    def connect(self):
+        '''Connect to server.'''
+        self.disconnect()
+        try:
+            self.connection = PyMyServerControl(self.host, self.port,
+                self.username, self.password)
+        except socket.error:
+            return False
+        return True
+
+    def disconnect(self):
+        '''Disconnect from server.'''
+        if self.connection:
+            self.connection.close()
+            self.connection = None
+        return True
+
+    def reboot(self):
+        '''Reboot server.'''
+        if self.connection:
+            self.connection.send_header('REBOOT', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return True
+        else:
+            return False
+
+    def version(self):
+        '''Get version string.'''
+        if self.connection:
+            self.connection.send_header('VERSION', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return self.connection.read()
+        else:
+            return False
+
+    def enable_reboot(self):
+        '''Enable server auto-reboot.'''
+        if self.connection:
+            self.connection.send_header('ENABLEREBOOT', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return True
+        else:
+            return False
+
+    def disable_reboot(self):
+        '''Disable server auto-reboot.'''
+        if self.connection:
+            self.connection.send_header('DISABLEREBOOT', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return True
+        else:
+            return False
+
+    def show_connections(self):
+        '''List active server connections.'''
+        if self.connection:
+            self.connection.send_header('SHOWCONNECTIONS', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return self.parser.connections(self.connection.read())
+        else:
+            return False
+
+    def kill_connection(self, num):
+        '''Kill connection to server.'''
+        if self.connection:
+            self.connection.send_header('KILLCONNECTION', 0, str(num))
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return True
+        else:
+            return False
+
+    def show_language_files(self):
+        '''List available language files.'''
+        if self.connection:
+            self.connection.send_header('SHOWLANGUAGEFILES', 0)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return self.parser.language_files(self.connection.read())
+        else:
+            return False
+
+    def get_file(self, file_name):
+        '''Get file from server.'''
+        if self.connection:
+            self.connection.send_header('GETFILE', 0, file_name)
+            self.connection.read_header()
+            self.response_code = self.connection.response_code
+            if self.response_code != '100':
+                return False
+            return self.connection.read()
+        else:
+            return False
+
+    def get_vhost_configuration(self):
+        '''Get virtual hosts configuration wrapped in list of VHostWrapper
+        objects.'''
+        text = self.get_file('virtualhosts.xml')
+        if text is not False:
+            return make_vhost_list(text)
+        return False
+

-----------------------------------------------------------------------

Summary of changes:
 misc/PyGTK_Control/ConfigGUI.py                    |  372 ----
 misc/PyGTK_Control/MIMEtypes.py                    |   61 -
 misc/PyGTK_Control/MIMEtypes.xml                   |  985 -----------
 misc/PyGTK_Control/MyServer Configure.ico          |  Bin 2238 -> 0 bytes
 misc/PyGTK_Control/MyServerControl.py              |  461 +++++
 misc/PyGTK_Control/PyGTKControl.glade              | 1436 ++++++++++++++++
 misc/PyGTK_Control/XMLGui.glade                    | 1810 --------------------
 misc/py_control_client/INSTALL                     |   11 +
 .../py_control_client/MyServer/GUI/AboutWindow.py  |   21 +-
 .../MyServer/GUI/BrowserWidgets.py                 |   80 +
 .../MyServer/GUI/ConnectionWindow.py               |   45 +
 .../MyServer/GUI/DefinitionWidgets.py              |  304 ++++
 misc/py_control_client/MyServer/GUI/GUIConfig.py   |   91 +
 misc/py_control_client/MyServer/GUI/MIMEWidgets.py |  204 +++
 .../MyServer/GUI/SecurityWidgets.py                |  732 ++++++++
 .../py_control_client/MyServer/GUI/VHostWidgets.py |  515 ++++++
 .../{pycontrol => MyServer/GUI}/__init__.py        |    0
 misc/py_control_client/{ => MyServer}/README       |    0
 .../MyServer}/__init__.py                          |    0
 .../MyServer/pycontrol}/__init__.py                |    0
 .../{ => MyServer}/pycontrol/pycontrol.py          |   20 +-
 .../MyServer/pycontrollib}/__init__.py             |    1 -
 .../MyServer/pycontrollib/browser.py               |   86 +
 .../MyServer/pycontrollib/config.py                |   70 +
 .../MyServer/pycontrollib/controller.py            |  145 ++
 .../MyServer/pycontrollib/definition.py            |  252 +++
 .../py_control_client/MyServer/pycontrollib/log.py |  242 +++
 .../MyServer/pycontrollib/mimetypes.py             |  270 +++
 .../MyServer/pycontrollib/security.py              |  474 +++++
 .../MyServer/pycontrollib/test/Makefile            |   45 +
 .../MyServer/pycontrollib/test/config_test.py      |  134 ++
 .../MyServer/pycontrollib/test/definition_test.py  |  481 ++++++
 .../MyServer/pycontrollib/test/log_test.py         |  425 +++++
 .../MyServer/pycontrollib/test/mimetypes_test.py   |  484 ++++++
 .../MyServer/pycontrollib/test/security_test.py    | 1122 ++++++++++++
 .../MyServer/pycontrollib/test/vhost_test.py       |  623 +++++++
 .../MyServer/pycontrollib/vhost.py                 |  306 ++++
 misc/py_control_client/{ => MyServer}/sample.py    |    0
 .../nodetree.h => misc/py_control_client/setup.py  |   16 +-
 39 files changed, 9070 insertions(+), 3254 deletions(-)
 delete mode 100755 misc/PyGTK_Control/ConfigGUI.py
 delete mode 100644 misc/PyGTK_Control/MIMEtypes.py
 delete mode 100644 misc/PyGTK_Control/MIMEtypes.xml
 delete mode 100755 misc/PyGTK_Control/MyServer Configure.ico
 create mode 100644 misc/PyGTK_Control/MyServerControl.py
 create mode 100644 misc/PyGTK_Control/PyGTKControl.glade
 delete mode 100644 misc/PyGTK_Control/XMLGui.glade
 create mode 100644 misc/py_control_client/INSTALL
 copy myserver/include/conf/nodetree.h => 
misc/py_control_client/MyServer/GUI/AboutWindow.py (63%)
 create mode 100644 misc/py_control_client/MyServer/GUI/BrowserWidgets.py
 create mode 100644 misc/py_control_client/MyServer/GUI/ConnectionWindow.py
 create mode 100644 misc/py_control_client/MyServer/GUI/DefinitionWidgets.py
 create mode 100644 misc/py_control_client/MyServer/GUI/GUIConfig.py
 create mode 100644 misc/py_control_client/MyServer/GUI/MIMEWidgets.py
 create mode 100644 misc/py_control_client/MyServer/GUI/SecurityWidgets.py
 create mode 100644 misc/py_control_client/MyServer/GUI/VHostWidgets.py
 rename misc/py_control_client/{pycontrol => MyServer/GUI}/__init__.py (100%)
 rename misc/py_control_client/{ => MyServer}/README (100%)
 copy misc/{myserver-get/myserverGetLib => 
py_control_client/MyServer}/__init__.py (100%)
 copy misc/{myserver-get/myserverGetLib => 
py_control_client/MyServer/pycontrol}/__init__.py (100%)
 rename misc/py_control_client/{ => MyServer}/pycontrol/pycontrol.py (87%)
 copy misc/{myserver-get/myserverGetLib => 
py_control_client/MyServer/pycontrollib}/__init__.py (66%)
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/browser.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/config.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/controller.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/definition.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/log.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/mimetypes.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/security.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/test/Makefile
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/config_test.py
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/definition_test.py
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/log_test.py
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/mimetypes_test.py
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/security_test.py
 create mode 100644 
misc/py_control_client/MyServer/pycontrollib/test/vhost_test.py
 create mode 100644 misc/py_control_client/MyServer/pycontrollib/vhost.py
 rename misc/py_control_client/{ => MyServer}/sample.py (100%)
 copy myserver/include/conf/nodetree.h => misc/py_control_client/setup.py (74%)


hooks/post-receive
-- 
GNU MyServer




reply via email to

[Prev in Thread] Current Thread [Next in Thread]