Index: pykoticon/tags/1.02/tests/test.py
===================================================================
--- pykoticon/tags/1.02/tests/test.py (revision 107)
+++ pykoticon/tags/1.02/tests/test.py (revision 107)
@@ -0,0 +1,70 @@
+#! /usr/bin/env python
+# -*- coding: ISO-8859-15 -*-
+
+# PyKotIcon - an end-user companion for PyKota
+#
+# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+#
+#
+
+import sys
+import os
+import time
+import xmlrpclib
+
+def main(arguments) :
+    """Main function."""
+    printername = os.environ.get("PYKOTAPRINTERNAME", "Unknown")
+    username = os.environ.get("PYKOTAUSERNAME", "Unknown")
+    jobid = os.environ.get("PYKOTAJOBID", "Unknown")
+    jobtitle = os.environ.get("PYKOTATITLE", "Unknown")
+    jobsize = os.environ.get("PYKOTAPRECOMPUTEDJOBSIZE", "Unknown")
+    billingcode = os.environ.get("PYKOTAJOBBILLING", "")
+    if len(arguments) < 3 :
+        message = """Hello %(username)s,
+        
+You sent job %(jobid)s (%(jobtitle)s) to printer %(printername)s.
+
+This job seems to be %(jobsize)s pages long. 
+
+Do you really want to print it ?""" % locals()
+        yesno = True
+    else :
+        message = "\n".join(arguments[2:])
+        yesno = False
+
+    server = xmlrpclib.ServerProxy("http://%s:%s" % (arguments[0], arguments[1]))
+    #result = server.showDialog(xmlrpclib.Binary(message), yesno)
+    #"""
+    result = server.askDatas([xmlrpclib.Binary(v) for v in ["Username", "Password", "Billing code"]], \
+                             ["username", "password", "billingcode"], \
+                             {"username": xmlrpclib.Binary(username), \
+                              "password": xmlrpclib.Binary(""), \
+                              "billingcode" : xmlrpclib.Binary(billingcode)})
+    #"""                          
+    #server.quitApplication()
+    if result["isValid"] :
+        print "\n".join(["%s => '%s'" % (k, v.data) for (k, v) in result.items() if k != "isValid"])
+    else :    
+        print "the end user closed the dialog box !"
+        
+if __name__ == "__main__" :
+    if len(sys.argv) < 3 :
+        sys.stderr.write("usage : %s printing_client_hostname_or_ip_address printing_client_TCPPort\n" % sys.argv[0])
+    else :    
+        main(sys.argv[1:])
Index: pykoticon/tags/1.02/TODO
===================================================================
--- pykoticon/tags/1.02/TODO (revision 157)
+++ pykoticon/tags/1.02/TODO (revision 157)
@@ -0,0 +1,28 @@
+# $Id$
+
+PyKotIcon - Client side helper for PyKota and other applications
+
+(c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+============================================================
+
+TODO, in no particular order :
+
+    - SSL support.
+    
+============================================================
+
+Please e-mail bugs to: alet@librelogiciel.com (Jerome Alet)
Index: pykoticon/tags/1.02/setup.py
===================================================================
--- pykoticon/tags/1.02/setup.py (revision 156)
+++ pykoticon/tags/1.02/setup.py (revision 156)
@@ -0,0 +1,66 @@
+#! /usr/bin/env python
+# -*- coding: ISO-8859-15 -*-
+
+"""Packaging and installation script for PyKotIcon."""
+
+# PyKotIcon - Client side helper for PyKota and other applications
+#
+# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+#
+#
+
+from distutils.core import setup
+import sys
+import os
+import imp
+import glob
+try :
+    import py2exe
+except ImportError :
+    if sys.platform == "win32" :
+        sys.stderr.write("py2exe is not installed ! ABORTING.\n")
+        sys.exit(-1)
+    else :    
+        directory = os.sep.join(["share", "pykoticon"])
+        mandir = os.sep.join(["share", "man", "man1"])
+        manpages = glob.glob(os.sep.join(["man", "*.1"]))    
+        initialdatafiles = [(mandir, manpages)]
+        withPy2EXE = False
+else :        
+    directory = "."
+    initialdatafiles = []
+    withPy2EXE = True
+
+config = imp.load_source("config", os.path.join("bin", "pykoticon"))
+setupDictionary = { "name" : "pykoticon", 
+                    "version" : config.__version__,
+                    "license" : config.__license__,
+                    "description" : config.__doc__,
+                    "author" : config.__author__,
+                    "author_email" : config.__author_email__,
+                    "url" : config.__url__,
+                    "data_files" : initialdatafiles \
+                      + [(directory, glob.glob(os.path.join("icons", "*.ico")))],
+                  }
+if withPy2EXE :
+    setupDictionary["windows"] = [ os.path.join("bin", "pykoticon") ]
+    setupDictionary["data_files"].append((directory, [os.path.join("bin", "pykoticon.vbs")]))
+    setupDictionary["data_files"].append((directory, [os.path.join(".", "COPYING")]))
+else :
+    setupDictionary["scripts"] = [os.path.join("bin", "pykoticon")]
+setup(**setupDictionary)
Index: pykoticon/tags/1.02/COPYING
===================================================================
--- pykoticon/tags/1.02/COPYING (revision 49)
+++ pykoticon/tags/1.02/COPYING (revision 49)
@@ -0,0 +1,340 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          59 Temple Place, Suite 330
+                          Boston, MA 02111-1307, USA.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+                     END OF TERMS AND CONDITIONS
+
+        Appendix: 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
Index: pykoticon/tags/1.02/bin/pykoticon
===================================================================
--- pykoticon/tags/1.02/bin/pykoticon (revision 154)
+++ pykoticon/tags/1.02/bin/pykoticon (revision 154)
@@ -0,0 +1,511 @@
+#! /usr/bin/env python
+# -*- coding: ISO-8859-15 -*-
+
+"""PyKotIcon is a generic, networked, cross-platform dialog box manager."""
+
+# PyKotIcon - Client side helper for PyKota and other applications
+#
+# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+__version__ = "1.02"
+__author__ = "Jerome Alet"
+__author_email__ = "alet@librelogiciel.com"
+__license__ = "GNU GPL"
+__url__ = "http://www.pykota.com/software/pykoticon"
+__revision__ = "$Id$"
+
+import sys
+import os
+import time
+import urllib
+import urllib2
+import locale
+import gettext
+import socket
+import threading
+import xmlrpclib
+import SimpleXMLRPCServer
+try :
+    import optparse
+except ImportError :    
+    sys.stderr.write("You need Python v2.3 or higher for PyKotIcon to work.\nAborted.\n")
+    sys.exit(-1)
+
+if sys.platform == "win32" :
+    isWindows = True
+    try :
+        import win32api
+    except ImportError :    
+        raise ImportError, "Mark Hammond's Win32 Extensions are missing. Please install them."
+    else :    
+        iconsdir = os.path.split(sys.argv[0])[0]
+else :        
+    isWindows = False
+    iconsdir = "/usr/share/pykoticon"   # TODO : change this
+    import pwd
+    
+try :    
+    import wx
+    hasWxPython = True
+except ImportError :    
+    hasWxPython = False
+    raise ImportError, "wxPython is missing. Please install it."
+    
+aboutbox = """PyKotIcon v%(__version__)s (c) 2003-2006 %(__author__)s - %(__author_email__)s
+
+PyKotIcon is generic, networked, cross-platform dialog box manager.
+
+It is often used as a client side companion for PyKota, but it
+can be used from other applications if you want.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA."""
+
+        
+class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer) :
+    """My own server class."""
+    allow_reuse_address = True
+    def __init__(self, frame, options, arguments) :
+        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, \
+                                                       ('0.0.0.0', options.port), \
+                                                       SimpleXMLRPCServer.SimpleXMLRPCRequestHandler, \
+                                                       options.debug)
+        self.frame = frame
+        self.debug = options.debug
+        self.cacheduration = options.cache
+        self.cache = {}
+        self.printServers = [ socket.gethostbyname(arg) for arg in arguments ]
+        if "127.0.0.1" not in self.printServers :
+            self.printServers.append("127.0.0.1") # to allow clean shutdown
+        loop = threading.Thread(target=self.mainloop)
+        loop.start()
+        
+    def logDebug(self, message) :    
+        """Logs a debug message if debug mode is active."""
+        if self.debug :
+            sys.stderr.write("%s\n" % message)
+            
+    def getAnswerFromCache(self, key) :
+        """Tries to extract a value from the cache and returns it if still valid."""
+        cacheentry = self.cache.get(key)
+        if cacheentry is not None :
+            (birth, value) = cacheentry 
+            if (time.time() - birth) < self.cacheduration :
+                self.logDebug("Cache hit for %s" % str(key))
+                return value # NB : we don't extend the life of this entry
+            else :    
+                self.logDebug("Cache expired for %s" % str(key))
+        else :        
+            self.logDebug("Cache miss for %s" % str(key))
+        return None
+        
+    def storeAnswerInCache(self, key, value) :    
+        """Stores an entry in the cache."""
+        self.cache[key] = (time.time(), value)
+        self.logDebug("Cache store for %s" % str(key))
+        
+    def export_askDatas(self, labels, varnames, varvalues) :
+        """Asks some textual datas defined by a list of labels, a list of variables' names and a list of variables values in a mapping."""
+        values = {}
+        for (key, value) in varvalues.items() :
+            values[key] = self.frame.UTF8ToUserCharset(value.data)
+        cachekey = tuple(values.items())  
+        retcode = self.getAnswerFromCache(cachekey)
+        if (retcode is None) or (not retcode["isValid"]) :
+            wx.CallAfter(self.frame.askDatas, [ self.frame.UTF8ToUserCharset(label.data) for label in labels ], \
+                                              varnames, \
+                                              values)
+            # ugly, isn't it ?
+            while self.frame.dialogAnswer is None :
+                time.sleep(0.1)
+            retcode = self.frame.dialogAnswer    
+            for (key, value) in retcode.items() :
+                if key != "isValid" :
+                    retcode[key] = xmlrpclib.Binary(self.frame.userCharsetToUTF8(value))
+            self.frame.dialogAnswer = None # prepare for next call, just in case
+            self.storeAnswerInCache(cachekey, retcode)
+        return retcode
+        
+    def export_quitApplication(self) :    
+        """Makes the application quit."""
+        self.frame.quitEvent.set()
+        wx.CallAfter(self.frame.OnClose, None)
+        return True
+        
+    def export_showDialog(self, message, yesno) :
+        """Opens a notification or confirmation dialog."""
+        wx.CallAfter(self.frame.showDialog, self.frame.UTF8ToUserCharset(message.data), yesno)
+        # ugly, isn't it ?
+        while self.frame.dialogAnswer is None :
+            time.sleep(0.1)
+        retcode = self.frame.dialogAnswer    
+        self.frame.dialogAnswer = None # prepare for next call, just in case
+        return retcode
+        
+    def export_nop(self) :    
+        """Does nothing, but allows a clean shutdown from the frame itself."""
+        return True
+        
+    def _dispatch(self, method, params) :    
+        """Ensure that only export_* methods are available."""
+        return getattr(self, "export_%s" % method)(*params)
+        
+    def handle_error(self, request, client_address) :    
+        """Doesn't display an ugly traceback in case an error occurs."""
+        self.logDebug("An exception occured while handling an incoming request from %s:%s" % (client_address[0], client_address[1]))
+        
+    def verify_request(self, request, client_address) :
+        """Ensures that requests which don't come from the print server are rejected."""
+        (client, port) = client_address
+        if client in self.printServers :
+            self.logDebug("%s accepted." % client)
+            return True
+        else :
+            # Unauthorized access !
+            self.logDebug("%s rejected." % client)
+            return False
+        
+    def mainloop(self) :
+        """XML-RPC Server's main loop."""
+        self.register_function(self.export_askDatas)
+        self.register_function(self.export_showDialog)
+        self.register_function(self.export_quitApplication)
+        self.register_function(self.export_nop)
+        while not self.frame.quitEvent.isSet() :
+            self.handle_request()
+        self.server_close()    
+        sys.exit(0)
+    
+    
+class GenericInputDialog(wx.Dialog) :
+    """Generic input dialog box."""
+    def __init__(self, parent, id, labels, varnames, varvalues):
+        wx.Dialog.__init__(self, parent, id, \
+               _("PyKotIcon data input"), \
+               style = wx.CAPTION \
+                     | wx.THICK_FRAME \
+                     | wx.STAY_ON_TOP \
+                     | wx.DIALOG_MODAL)
+
+        self.variables = []
+        vsizer = wx.BoxSizer(wx.VERTICAL)
+        for i in range(len(varnames)) :
+            varname = varnames[i]
+            try :
+                label = labels[i]
+            except IndexError :    
+                label = ""
+            labelid = wx.NewId()    
+            varid = wx.NewId()
+            labelst = wx.StaticText(self, labelid, label)
+            if varname.lower().find("password") != -1 :
+                variable = wx.TextCtrl(self, varid, varvalues.get(varname, ""), style=wx.TE_PASSWORD)
+            else :
+                variable = wx.TextCtrl(self, varid, varvalues.get(varname, ""))
+            self.variables.append(variable)    
+            hsizer = wx.BoxSizer(wx.HORIZONTAL)
+            hsizer.Add(labelst, 0, wx.ALIGN_CENTER | wx.ALIGN_RIGHT | wx.ALL, 5)
+            hsizer.Add(variable, 0, wx.ALIGN_CENTER | wx.ALIGN_LEFT | wx.ALL, 5)
+            vsizer.Add(hsizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
+            
+        okbutton = wx.Button(self, wx.ID_OK, "OK")    
+        vsizer.Add(okbutton, 0, wx.ALIGN_CENTER | wx.ALL, 5)
+        
+        self.SetAutoLayout(True)
+        self.SetSizerAndFit(vsizer)
+        self.Layout()
+        
+        
+class PyKotIcon(wx.Frame):
+    """Main class."""
+    def __init__(self, parent, id):
+        self.dialogAnswer = None
+        wx.Frame.__init__(self, parent, id, \
+               _("PyKotIcon info for %s") % self.getCurrentUserName(), \
+               size = (0, 0), \
+               style = wx.FRAME_NO_TASKBAR | wx.NO_FULL_REPAINT_ON_RESIZE)
+                     
+        self.tbicon = wx.TaskBarIcon()
+        self.greenicon = wx.Icon(os.path.join(iconsdir, "pykoticon-green.ico"), \
+                                  wx.BITMAP_TYPE_ICO)
+        self.redicon = wx.Icon(os.path.join(iconsdir, "pykoticon-red.ico"), \
+                                  wx.BITMAP_TYPE_ICO)
+        self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
+        
+        wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
+        wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
+        
+        self.TBMENU_ABOUT = wx.NewId()
+        self.TBMENU_RESTORE = wx.NewId()
+        self.TBMENU_CLOSE = wx.NewId()
+        wx.EVT_MENU(self.tbicon, self.TBMENU_ABOUT, \
+                                 self.OnAbout)
+        wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
+                                 self.OnTaskBarActivate)
+        wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
+                                 self.OnTaskBarClose)
+        self.menu = wx.Menu()
+        self.menu.Append(self.TBMENU_ABOUT, _("About"))
+        self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
+        
+        wx.EVT_ICONIZE(self, self.OnIconify)
+        wx.EVT_CLOSE(self, self.OnClose)
+        self.Show(True)
+        self.Iconize()
+        self.Hide()
+        
+    def getCurrentUserName(self) :
+        """Retrieves the current user's name."""
+        if isWindows :
+            return win32api.GetUserName()
+        else :    
+            try :
+                return pwd.getpwuid(os.geteuid())[0]
+            except :
+                return "** Unknown **"
+            
+    def OnIconify(self, event) :
+        """Iconify/De-iconify the application."""
+        if not self.IsIconized() :
+            self.Iconize(True)
+        self.Hide()
+
+    def OnTaskBarActivate(self, event) :
+        """Show the application if it is minimized."""
+        if self.IsIconized() :
+            self.Iconize(False)
+        if not self.IsShown() :
+            self.Show(True)
+        self.Raise()
+
+    def OnClose(self, event) :
+        """Cleanly quit the application."""
+        if (event is None) or self.options.allowquit :
+            self.closeServer()
+            self.menu.Destroy()
+            self.tbicon.Destroy()
+            self.Destroy()
+        else :    
+            self.quitIsForbidden()
+
+    def OnTaskBarMenu(self, event) :
+        """Open the taskbar menu."""
+        self.tbicon.PopupMenu(self.menu)
+
+    def OnTaskBarClose(self, event) :
+        """React to close from the taskbar."""
+        if self.options.allowquit :
+            self.Close()
+        else :
+            self.quitIsForbidden()
+            
+    def quitIsForbidden(self) :        
+        """Displays a message indicating that quitting the application is not allowed."""
+        message = _("Sorry, this was forbidden by your system administrator.")
+        caption = _("Information")
+        style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
+        dialog = wx.MessageDialog(self, message, caption, style)
+        dialog.ShowModal()
+        dialog.Destroy()
+        
+    def OnAbout(self, event) :    
+        """Displays the about box."""
+        dialog = wx.MessageDialog(self, aboutbox % globals(), \
+                                        _("About"), \
+                                        wx.OK | wx.ICON_INFORMATION)
+        dialog.ShowModal()
+        dialog.Destroy()
+        
+    def showDialog(self, message, yesno) :
+        """Opens a notification dialog."""
+        self.dialogAnswer = None
+        if yesno :
+            caption = _("Confirmation")
+            style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION
+        else :
+            caption = _("Information")
+            style = wx.OK | wx.ICON_INFORMATION
+        style |= wx.STAY_ON_TOP    
+        dialog = wx.MessageDialog(self, message, caption, style)
+        self.dialogAnswer = ((dialog.ShowModal() == wx.ID_NO) and "CANCEL") or "OK"
+        dialog.Destroy()
+        
+    def askDatas(self, labels, varnames, varvalues) :
+        """Opens a dialog box asking for data entry."""
+        self.dialogAnswer = None
+        dialog = GenericInputDialog(self, wx.ID_ANY, labels, varnames, varvalues)
+        retvalues = {}
+        if dialog.ShowModal() == wx.ID_OK :
+            retvalues["isValid"] = True
+            for i in range(len(varnames)) :
+                retvalues[varnames[i]] = dialog.variables[i].GetValue()
+        else :        
+            retvalues["isValid"] = False
+            for k in varvalues.keys() :
+                retvalues[k] = ""
+        self.dialogAnswer = retvalues
+        dialog.Destroy()
+        
+    def closeServer(self) :    
+        """Tells the xml-rpc server to exit."""
+        if not self.quitEvent.isSet() :
+            self.quitEvent.set()
+        server = xmlrpclib.ServerProxy("http://localhost:%s" % self.options.port)    
+        try :
+            # wake the server with an empty request 
+            # for it to see the event object
+            # which has just been set
+            server.nop()
+        except :    
+            # Probably already stopped
+            pass
+        
+    def postInit(self, charset, options, arguments) :    
+        """Starts the XML-RPC server."""
+        self.quitEvent = threading.Event()
+        self.charset = charset
+        self.options = options
+        self.server = MyXMLRPCServer(self, options, arguments)
+        
+    def UTF8ToUserCharset(self, text) :
+        """Converts from UTF-8 to user's charset."""
+        if text is not None :
+            try :
+                return text.decode("UTF-8").encode(self.charset, "replace") 
+            except (UnicodeError, AttributeError) :    
+                try :
+                    # Maybe already in Unicode
+                    return text.encode(self.charset, "replace") 
+                except (UnicodeError, AttributeError) :
+                    pass # Don't know what to do
+        return text
+        
+    def userCharsetToUTF8(self, text) :
+        """Converts from user's charset to UTF-8."""
+        if text is not None :
+            try :
+                # We don't necessarily trust the default charset, because
+                # xprint sends us titles in UTF-8 but CUPS gives us an ISO-8859-1 charset !
+                # So we first try to see if the text is already in UTF-8 or not, and
+                # if it is, we delete characters which can't be converted to the user's charset,
+                # then convert back to UTF-8. PostgreSQL 7.3.x used to reject some unicode characters,
+                # this is fixed by the ugly line below :
+                return text.decode("UTF-8").encode(self.charset, "replace").decode(self.charset).encode("UTF-8", "replace")
+            except (UnicodeError, AttributeError) :
+                try :
+                    return text.decode(self.charset).encode("UTF-8", "replace") 
+                except (UnicodeError, AttributeError) :    
+                    try :
+                        # Maybe already in Unicode
+                        return text.encode("UTF-8", "replace") 
+                    except (UnicodeError, AttributeError) :
+                        pass # Don't know what to do
+        return text
+        
+
+class PyKotIconApp(wx.App):
+    def OnInit(self) :
+        self.frame = PyKotIcon(None, wx.ID_ANY)
+        self.frame.Show(False)
+        self.SetTopWindow(self.frame)
+        return True
+        
+    def postInit(self, charset, options, arguments) :    
+        """Continues processing."""
+        self.frame.postInit(charset, options, arguments)
+        
+        
+def main() :
+    """Program's entry point."""
+    try :
+        locale.setlocale(locale.LC_ALL, "")
+    except (locale.Error, IOError) :
+        sys.stderr.write("Problem while setting locale.\n")
+    try :
+        gettext.install("pykoticon")
+    except :
+        gettext.NullTranslations().install()
+        
+    localecharset = None
+    try :
+        try :
+            localecharset = locale.nl_langinfo(locale.CODESET)
+        except AttributeError :    
+            try :
+                localecharset = locale.getpreferredencoding()
+            except AttributeError :    
+                try :
+                    localecharset = locale.getlocale()[1]
+                    localecharset = localecharset or locale.getdefaultlocale()[1]
+                except ValueError :    
+                    pass        # Unknown locale, strange...
+    except locale.Error :            
+        pass
+    charset = os.environ.get("CHARSET") or localecharset or "ISO-8859-15"
+    
+    parser = optparse.OptionParser(usage="usage : pykoticon [options] server1 [server2 ...]")
+    parser.add_option("-v", "--version", 
+                            action="store_true", 
+                            dest="version",
+                            help=_("show PyKotIcon's version number and exit."))
+    parser.add_option("-c", "--cache", 
+                            type="int", 
+                            default=0, 
+                            dest="cache",
+                            help=_("the duration of the cache in seconds to keep input forms' datas in memory. Defaults to 0 second, meaning no cache."))
+    parser.add_option("-d", "--debug", 
+                            action="store_true", 
+                            dest="debug",
+                            help=_("activate debug mode."))
+    parser.add_option("-p", "--port", 
+                            type="int", 
+                            default=7654, 
+                            dest="port",
+                            help=_("the TCP port PyKotIcon will listen to, default is 7654."))
+    parser.add_option("-q", "--allowquit", 
+                            action="store_true", 
+                            dest="allowquit",
+                            help=_("allow the end user to close the application."))
+    (options, arguments) = parser.parse_args()
+    if options.version :
+        print "PyKotIcon v%(__version__)s" % globals()
+    else :
+        if not (1024 <= options.port <= 65535) :
+            sys.stderr.write(_("The TCP port number specified for --port must be between 1024 and 65535.\n"))
+        elif not (0 <= options.cache <= 86400) :    
+            sys.stderr.write(_("The duration specified for --cache must be between 0 and 86400 seconds.\n"))
+        else :    
+            app = PyKotIconApp()
+            app.postInit(charset, options, arguments)
+            app.MainLoop()
+    
+    
+if __name__ == '__main__':
+    main()
+    
Index: pykoticon/tags/1.02/bin/pykoticon.vbs
===================================================================
--- pykoticon/tags/1.02/bin/pykoticon.vbs (revision 125)
+++ pykoticon/tags/1.02/bin/pykoticon.vbs (revision 125)
@@ -0,0 +1,26 @@
+' PyKotIcon - Client side helper for PyKota
+'
+' (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+' This program is free software; you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation; either version 2 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY; without even the implied warranty of
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+' GNU General Public License for more details.
+' 
+' You should have received a copy of the GNU General Public License
+' along with this program; if not, write to the Free Software
+' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+'
+' $Id$
+'
+'
+' Edit the last line of this file to add all your print servers
+' hostnames or IP addresses instead of "nordine.ateur" and optionally
+' define another TCP port to listen to.
+'
+set Wshshell= WScript.createobject("wscript.shell")
+retcode = Wshshell.run ("%comspec% /C pykoticon.exe --port 7654 nordine.ateur", 0, FALSE)
Index: pykoticon/tags/1.02/MANIFEST.in
===================================================================
--- pykoticon/tags/1.02/MANIFEST.in (revision 144)
+++ pykoticon/tags/1.02/MANIFEST.in (revision 144)
@@ -0,0 +1,5 @@
+include README COPYING TODO NEWS CREDITS MANIFEST.in clean.sh bin/pykoticon bin/pykoticon.vbs
+recursive-include po README *.sh *.po *.mo *.pot
+recursive-include icons *.ico
+recursive-include man README *.sh *.1
+recursive-include screenshots *.png *.jpeg
Index: pykoticon/tags/1.02/clean.sh
===================================================================
--- pykoticon/tags/1.02/clean.sh (revision 141)
+++ pykoticon/tags/1.02/clean.sh (revision 141)
@@ -0,0 +1,31 @@
+#! /bin/sh
+#
+# PyKotIcon - Client side helper for PyKota and other applications
+#
+# (c) 2005 Jerome Alet <alet@librelogiciel.com>
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+# Use this to clean the tree from temporary files
+
+rm -fr MANIFEST build dist
+rm -f bin/pykoticonc
+find . -name "*.bak" -exec rm -f {} \;
+find . -name "*~" -exec rm -f {} \;
+find . -name "*.pyc" -exec rm -f {} \;
+find . -name "*.pyo" -exec rm -f {} \;
+find . -name "*.jem" -exec rm -f {} \;
+find . -name "*.tmp" -exec rm -f {} \;
Index: pykoticon/tags/1.02/man/genman.sh
===================================================================
--- pykoticon/tags/1.02/man/genman.sh (revision 140)
+++ pykoticon/tags/1.02/man/genman.sh (revision 140)
@@ -0,0 +1,20 @@
+#! /bin/sh
+#
+# PyKotIcon - Client side helper for PyKota and other applications
+#
+# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+# You're welcome to redistribute this software under the
+# terms of the GNU General Public Licence version 2.0
+# or, at your option, any higher version.
+#
+# You can read the complete GNU GPL in the file COPYING
+# which should come along with this software, or visit
+# the Free Software Foundation's WEB site http://www.fsf.org
+#
+# $Id$
+#
+for prog in pykoticon ; do 
+    echo $prog ;
+    help2man --no-info --section=1 --manual "User Commands" --source="C@LL - Conseil Internet & Logiciels Libres" --output=$prog.1 $prog ; 
+    echo ;
+done
Index: pykoticon/tags/1.02/man/pykoticon.1
===================================================================
--- pykoticon/tags/1.02/man/pykoticon.1 (revision 140)
+++ pykoticon/tags/1.02/man/pykoticon.1 (revision 140)
@@ -0,0 +1,22 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.36.
+.TH PYKOTICON "1" "June 2006" "C@LL - Conseil Internet & Logiciels Libres" "User Commands"
+.SH NAME
+PyKotIcon \- manual page for PyKotIcon v1.02
+.SH DESCRIPTION
+usage: usage : pykoticon [options] server1 [server2 ...]
+.SS "options:"
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+show this help message and exit
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+show PyKotIcon's version number and exit
+.TP
+\fB\-d\fR, \fB\-\-debug\fR
+activate debug mode
+.TP
+\fB\-pPORT\fR, \fB\-\-port\fR=\fIPORT\fR
+the TCP port PyKotIcon will listen to, default is 7654
+.TP
+\fB\-q\fR, \fB\-\-allowquit\fR
+allow the end user to close the application
Index: pykoticon/tags/1.02/NEWS
===================================================================
--- pykoticon/tags/1.02/NEWS (revision 146)
+++ pykoticon/tags/1.02/NEWS (revision 146)
@@ -0,0 +1,48 @@
+# $Id$
+
+PyKotIcon - Client side helper for PyKota and other applications
+
+(c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+============================================================
+
+PyKotIcon NEWS :
+
+  - 1.02 (2006-06-07) :
+    
+    - Added the --cache command line option to specify the time in
+      seconds during which input forms' content will be cached.
+      This allows people to avoid retyping their username and
+      password for each print job, for example.
+      
+    - Now displays an error message when the end user wants to quit
+      and it is forbidden.
+      
+    - Doesn't display the ugly traceback anymore when the client  
+      disconnects before the server has sent the answer back
+      (pknotify --timeout).
+      
+  - 1.01 (2006-05-01) :
+      
+    - Now accepts command line options.    
+    
+    - Less ugly when run under *nix.
+    
+    - Debug mode off by default.
+    
+  - 1.00 (2006-03-23) : 
+  
+    - First release
Index: pykoticon/tags/1.02/CREDITS
===================================================================
--- pykoticon/tags/1.02/CREDITS (revision 142)
+++ pykoticon/tags/1.02/CREDITS (revision 142)
@@ -0,0 +1,20 @@
+# $Id$
+
+PyKotIcon - Client side helper for PyKota and other applications
+
+(c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+============================================================
Index: pykoticon/tags/1.02/README
===================================================================
--- pykoticon/tags/1.02/README (revision 158)
+++ pykoticon/tags/1.02/README (revision 158)
@@ -0,0 +1,181 @@
+# $Id$
+
+PyKotIcon - Client side helper for PyKota and other applications
+
+(c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+====================================================================
+
+PyKotIcon is a cross-platform client-side helper for PyKota.
+
+It is meant to be launched whenever the user logs in, and stay in 
+the taskbar (depending on the client operating system) until the 
+user closes his session. 
+  
+Installation :
+
+  * GNU/Linux and similar :
+  
+    $ cd pykoticon
+    $ python setup.py install
+    
+    Then modify your .xsession file, or the appropriate file depending
+    on your desktop environment, to ensure that the pykoticon command
+    is launched in the background each time your X Window session
+    starts.
+    
+    You MUST pass the list of hostnames or IP addresses from which
+    PyKotIcon should accept incoming connexions on pykoticon's
+    command line. Connexions from localhost are always accepted.
+    If you use PyKotIcon with PyKota, you'll want to pass the
+    hostnames or IP addresses of ALL print servers on pykoticon's
+    command line.
+    
+    NB : `pykoticon --help` will give you hints about the expected
+    command line parameters.
+    
+  * MS Windows :  
+  
+    Download pykoticon-x.yy.zip from 
+    
+        http://www.pykota.com/software/pykoticon/download/tarballs/
+        
+    Unzip it into a directory of its own. I usually unzip it into    
+    /var/lib/samba/netlogon/pykoticon/
+    
+    Modify the PYKOTICON.VBS file to include the list of authorized
+    print servers and change other parameters as needed, as explained
+    in the GNU/Linux section above.
+    
+    Ensure that PYKOTICON.VBS is executed each time an user logs
+    in. The easiest way to do this is in STARTUP.CMD :
+    
+      --- CUT ---
+      SET PATH=%PATH%;\\MYSERVER\NETLOGON\PYKOTICON\
+      \\MYSERVER\NETLOGON\PYKOTICON\PYKOTICON.VBS 
+      --- CUT ---
+      
+    NB : if you launch PYKOTICON.EXE directly instead of from
+    PYKOTICON.VBS, a small but disturbing window may appear on 
+    the screen.
+
+
+====================================================================
+
+
+Usage :
+
+  PyKotIcon is often used as a client side companion of the PyKota
+  print accounting solution, with PyKota's pknotify command line tool
+  acting as a PyKotIcon client. But in reality PyKotIcon is completely
+  independant of PyKota, and can be used from any application
+  which can do remote procedure calls over XML-RPC.
+  
+  PyKotIcon exposes 4 of its methods over XML-RPC, here they are :
+  
+    - nop :
+    
+      This methods does exactly nothing :)
+      
+      - Input : No argument.
+      
+      - Output : returns the True boolean value.
+      
+    - quitApplication :  
+    
+      This method causes the PyKotIcon application to exit.
+      
+      - Input : No argument.
+      
+      - Output : returns the True boolean value.
+          
+    - showDialog :  
+   
+      This methods displays an informational message, and returns
+      the choice the user made, if any.
+       
+      - Input : 
+        
+        - message : a Binary XML-RPC object representing an UTF-8 encoded 
+          character string which will be displayed to the end user. 
+          This string can contain literal "\n" sequences which will 
+          be automatically expanded into line breaks by PyKotIcon.
+          
+        - confirmation : a boolean value indicating if the user will  
+          be given the choice to valid or cancel (True), or only
+          to valid (False) the dialog box.
+            
+      - Output :      
+      
+        - the literal string "OK" if the user clicked on the OK button,
+          else the literal string "CANCEL". The latter is only possible
+          if the method was called with its second parameter set to
+          True.
+        
+    - askDatas :      
+    
+      This method can generate an input form and return what the user
+      entered in the different fields.
+      
+      - Input :
+      
+        - An array of labels, one label per input field in the 
+          form to be created. Each label in the list is passed as
+          a Binary XML-RPC object representing an UTF-8 encoded
+          character string.
+          
+        - An array of variables names, one name per input field in
+          the form to be created. Each name in the list is passed
+          as an ASCII encoded character string.
+          
+        - An array of initial values, one possibly empty value per  
+          input field in the form to be created. Each initial value
+          in the list is passed as a Binary XML-RPC object representing
+          an UTF-8 encoded character string.
+      
+      - Output :
+      
+        - An associative array (e.g. Python mapping) containing the
+          variables names as keys and the variables' contents as
+          values in the following format :
+          
+          - Each key is an ASCII encoded character string representing
+            the name of a variable to ask which was passed from the 
+            caller.
+            
+          - Each value is a Binary XML-RPC object representing an UTF-8  
+            encoded character string, itself being the result of user
+            input in the form's field for this particular variable.
+
+          - A special key named 'isValid' gives access to a boolean
+            value in the associative array. If the value is True,
+            then the input fields' contents are valid. If the value
+            is False, then was the user typed in the input fields
+            has to be discarded because, for example, the user
+            closed the form without validing his entries.
+            In this case, all the other values are empty strings
+            anyway.
+            
+            
+IMPORTANT : PyKotIcon doesn't currently support encrypted connexions,            
+so if you're afraid of sensitive information flying in the clear
+over the nework, you should probably install a transparent secure 
+tunneling software like stunnel on both sides of each connection.
+
+====================================================================
+    
+Please e-mail bugs to the PyKota mailing list at : pykota@librelogiciel.com
+or to the main author at : alet@librelogiciel.com (Jerome Alet)
